├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── 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-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── build │ └── XCBuildData │ │ ├── build.db │ │ ├── 7c3c4c189d1f80b76698c451bd5077cf-desc.xcbuild │ │ ├── ad56d1583fd8aa8a249efbeeed14e852-desc.xcbuild │ │ ├── b0a4c8cbd9a8dab09114fb1fa9023628-desc.xcbuild │ │ ├── eff7d1a8350f5f87a4e980a33ae8d563-desc.xcbuild │ │ └── BuildDescriptionCacheIndex-616f3cf5e93a195b4151706079281249 ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── project.pbxproj ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile.lock └── Podfile ├── lib ├── app │ ├── global.dart │ ├── locator.dart │ ├── route_names.dart │ └── navigation_service.dart ├── share │ ├── my_sliver_appbar_delegate.dart │ ├── grid_icon.dart │ ├── icon_add.dart │ ├── snapping_scroll_physics.dart │ ├── shared_style.dart │ ├── text.dart │ └── route.dart ├── main.dart ├── model │ ├── topic_model.dart │ └── user_model.dart └── screen │ ├── search_view.dart │ └── home_view.dart ├── screenshot └── home.png ├── fonts ├── Nunito-Bold.ttf ├── Nunito-Black.ttf ├── Nunito-Italic.ttf ├── Nunito-Light.ttf ├── Nunito-Regular.ttf ├── Nunito-ExtraBold.ttf ├── Nunito-SemiBold.ttf ├── Nunito-BlackItalic.ttf ├── Nunito-BoldItalic.ttf ├── Nunito-ExtraLight.ttf ├── Nunito-LightItalic.ttf ├── Nunito-SemiBoldItalic.ttf ├── Nunito-ExtraBoldItalic.ttf └── Nunito-ExtraLightItalic.ttf ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── 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 │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ └── values │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── ClubHouseClone │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── README.md ├── .metadata ├── .gitignore ├── test └── widget_test.dart ├── pubspec.yaml └── pubspec.lock /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/app/global.dart: -------------------------------------------------------------------------------- 1 | class Global { 2 | static Future init() async {} 3 | } 4 | -------------------------------------------------------------------------------- /screenshot/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/screenshot/home.png -------------------------------------------------------------------------------- /fonts/Nunito-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-Bold.ttf -------------------------------------------------------------------------------- /fonts/Nunito-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-Black.ttf -------------------------------------------------------------------------------- /fonts/Nunito-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-Italic.ttf -------------------------------------------------------------------------------- /fonts/Nunito-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-Light.ttf -------------------------------------------------------------------------------- /fonts/Nunito-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-Regular.ttf -------------------------------------------------------------------------------- /fonts/Nunito-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-ExtraBold.ttf -------------------------------------------------------------------------------- /fonts/Nunito-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-SemiBold.ttf -------------------------------------------------------------------------------- /fonts/Nunito-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-BlackItalic.ttf -------------------------------------------------------------------------------- /fonts/Nunito-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Nunito-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-ExtraLight.ttf -------------------------------------------------------------------------------- /fonts/Nunito-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/Nunito-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /ios/build/XCBuildData/build.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/build/XCBuildData/build.db -------------------------------------------------------------------------------- /fonts/Nunito-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Nunito-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/fonts/Nunito-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ClubHouseClone 2 | 3 | Clubhouse Clone in Flutter 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/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/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/build/XCBuildData/7c3c4c189d1f80b76698c451bd5077cf-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/build/XCBuildData/7c3c4c189d1f80b76698c451bd5077cf-desc.xcbuild -------------------------------------------------------------------------------- /ios/build/XCBuildData/ad56d1583fd8aa8a249efbeeed14e852-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/build/XCBuildData/ad56d1583fd8aa8a249efbeeed14e852-desc.xcbuild -------------------------------------------------------------------------------- /ios/build/XCBuildData/b0a4c8cbd9a8dab09114fb1fa9023628-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/build/XCBuildData/b0a4c8cbd9a8dab09114fb1fa9023628-desc.xcbuild -------------------------------------------------------------------------------- /ios/build/XCBuildData/eff7d1a8350f5f87a4e980a33ae8d563-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/build/XCBuildData/eff7d1a8350f5f87a4e980a33ae8d563-desc.xcbuild -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/ClubHouseClone/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.ClubHouseClone 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ios/build/XCBuildData/BuildDescriptionCacheIndex-616f3cf5e93a195b4151706079281249: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imchlorine/Flutter-Clubhouse-Clone/HEAD/ios/build/XCBuildData/BuildDescriptionCacheIndex-616f3cf5e93a195b4151706079281249 -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/app/locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | import 'package:ClubHouseClone/app/navigation_service.dart'; 3 | 4 | GetIt locator = GetIt.instance; 5 | 6 | void setupLocator() { 7 | locator.registerLazySingleton(() => NavigationService()); 8 | } 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 9b2d32b605630f28625709ebd9d78ab3016b2bf6 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/share/my_sliver_appbar_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class MySliverAppBarDelegate extends SliverPersistentHeaderDelegate { 4 | final PreferredSize child; 5 | 6 | MySliverAppBarDelegate({this.child}); 7 | 8 | @override 9 | Widget build( 10 | BuildContext context, double shrinkOffset, bool overlapsContent) { 11 | return child; 12 | } 13 | 14 | @override 15 | double get maxExtent => child.preferredSize.height; 16 | 17 | @override 18 | double get minExtent => child.preferredSize.height; 19 | 20 | @override 21 | bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) { 22 | return false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 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 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/share/grid_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:ClubHouseClone/share/shared_style.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | class GridIcon extends StatefulWidget { 5 | final Function onTap; 6 | const GridIcon({Key key, this.onTap}) : super(key: key); 7 | 8 | @override 9 | GridIconState createState() => GridIconState(); 10 | } 11 | 12 | class GridIconState extends State { 13 | bool showGridIcon; 14 | void changeState(value) { 15 | setState(() { 16 | showGridIcon = value; 17 | }); 18 | } 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | showGridIcon = true; 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Visibility( 29 | visible: showGridIcon, 30 | child: GestureDetector( 31 | onTap: widget.onTap, 32 | child: Icon( 33 | CupertinoIcons.circle_grid_3x3_fill, 34 | color: brown, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /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 (0.0.1): 7 | - Flutter 8 | - sqflite (0.0.2): 9 | - Flutter 10 | - FMDB (>= 2.7.5) 11 | 12 | DEPENDENCIES: 13 | - Flutter (from `Flutter`) 14 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 15 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 16 | 17 | SPEC REPOS: 18 | trunk: 19 | - FMDB 20 | 21 | EXTERNAL SOURCES: 22 | Flutter: 23 | :path: Flutter 24 | path_provider: 25 | :path: ".symlinks/plugins/path_provider/ios" 26 | sqflite: 27 | :path: ".symlinks/plugins/sqflite/ios" 28 | 29 | SPEC CHECKSUMS: 30 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 31 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 32 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c 33 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 34 | 35 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 36 | 37 | COCOAPODS: 1.10.1 38 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:ClubHouseClone/app/global.dart'; 2 | import 'package:ClubHouseClone/app/locator.dart'; 3 | import 'package:ClubHouseClone/app/navigation_service.dart'; 4 | import 'package:ClubHouseClone/app/route_names.dart'; 5 | import 'package:ClubHouseClone/screen/home_view.dart'; 6 | import 'package:flutter/cupertino.dart'; 7 | 8 | 9 | void main() { 10 | setupLocator(); 11 | WidgetsFlutterBinding.ensureInitialized(); 12 | Global.init().then((e) => runApp(MyApp())); 13 | } 14 | 15 | class MyApp extends StatelessWidget { 16 | // This widget is the root of your application. 17 | @override 18 | Widget build(BuildContext context) { 19 | return CupertinoApp( 20 | navigatorKey: locator().navigationKey, 21 | theme: CupertinoThemeData( 22 | textTheme: CupertinoTextThemeData( 23 | textStyle: TextStyle( 24 | color: CupertinoColors.black, 25 | fontFamily: 'Nunito', 26 | ), 27 | ), 28 | ), 29 | home: HomeView(), 30 | onGenerateRoute: generateRoute, 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/app/route_names.dart: -------------------------------------------------------------------------------- 1 | import 'package:ClubHouseClone/screen/home_view.dart'; 2 | import 'package:ClubHouseClone/screen/search_view.dart'; 3 | import 'package:ClubHouseClone/share/route.dart' as route; 4 | import 'package:flutter/cupertino.dart'; 5 | 6 | const String RootRoute = "/"; 7 | const String HomeViewRoute = "HomeView"; 8 | const String SearchViewRoute = "SearchView"; 9 | 10 | Route generateRoute(RouteSettings settings) { 11 | switch (settings.name) { 12 | case HomeViewRoute: 13 | return _getPageRouteCupertino( 14 | viewToShow: HomeView(), 15 | ); 16 | case SearchViewRoute: 17 | return _getPageRouteCupertino( 18 | viewToShow: SearchView(), 19 | ); 20 | default: 21 | return _getPageRouteCupertino( 22 | viewToShow: CupertinoPageScaffold( 23 | child: Center(child: Text('No route defined for ${settings.name}')), 24 | ), 25 | ); 26 | } 27 | } 28 | 29 | PageRoute _getPageRouteCupertino({Widget viewToShow}) { 30 | return route.CupertinoPageRoute( 31 | // fullscreenDialog: true, 32 | builder: (_) => viewToShow, 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /lib/app/navigation_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class NavigationService { 4 | GlobalKey _navigationKey = GlobalKey(); 5 | GlobalKey get navigationKey => _navigationKey; 6 | pop() { 7 | _navigationKey.currentState.pop(); 8 | } 9 | 10 | Future navigateTo(String routeName, {dynamic arguments}) { 11 | return _navigationKey.currentState 12 | .pushNamed(routeName, arguments: arguments); 13 | } 14 | 15 | Future navigateReplace(String routeName, {dynamic arguments}) { 16 | return _navigationKey.currentState 17 | .pushReplacementNamed(routeName, arguments: arguments); 18 | } 19 | 20 | Future popAndPushNamed(String routeName, {dynamic arguments}) { 21 | return _navigationKey.currentState 22 | .popAndPushNamed(routeName, arguments: arguments); 23 | } 24 | 25 | Future pushNamedAndRemoveUntil(String routeName, {dynamic arguments}) { 26 | return _navigationKey.currentState.pushNamedAndRemoveUntil( 27 | routeName, (Route route) => false, 28 | arguments: arguments); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:ClubHouseClone/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/model/topic_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:ClubHouseClone/model/user_model.dart'; 2 | 3 | class TopicModel { 4 | final String topicId; 5 | final String clubName; 6 | final String topicTitle; 7 | final List users; 8 | final int audienceNumber; 9 | final int speechNumber; 10 | 11 | TopicModel({ 12 | this.topicId, 13 | this.clubName, 14 | this.topicTitle, 15 | this.users, 16 | this.audienceNumber, 17 | this.speechNumber, 18 | }); 19 | } 20 | 21 | class TopicModelData { 22 | List topicData = [ 23 | TopicModel( 24 | topicId: "1", 25 | clubName: "My club", 26 | topicTitle: "How are you? Welcome to My room, let's have a talk!! ", 27 | users: UserModelData().userData1, 28 | audienceNumber: 1000, 29 | speechNumber: 5), 30 | TopicModel( 31 | topicId: "2", 32 | clubName: "TECH TALKS", 33 | topicTitle: "Flutter vs React Native", 34 | users: UserModelData().userData2, 35 | audienceNumber: 510, 36 | speechNumber: 10), 37 | TopicModel( 38 | topicId: "3", 39 | topicTitle: "Coffee", 40 | users: UserModelData().userData1, 41 | audienceNumber: 10, 42 | speechNumber: 2), 43 | ]; 44 | } 45 | -------------------------------------------------------------------------------- /lib/model/user_model.dart: -------------------------------------------------------------------------------- 1 | class UserModel { 2 | final String userId; 3 | final String userName; 4 | final String userImage; 5 | 6 | UserModel({ 7 | this.userId, 8 | this.userName, 9 | this.userImage, 10 | }); 11 | } 12 | 13 | class UserModelData { 14 | List userData1 = [ 15 | UserModel( 16 | userId: "3", 17 | userName: "Elon Mask", 18 | userImage: 19 | "https://content.fortune.com/wp-content/uploads/2021/02/GettyImages-1229901940.jpg", 20 | ), 21 | UserModel( 22 | userId: "2", 23 | userName: "Tim Cook", 24 | userImage: 25 | "https://photos5.appleinsider.com/gallery/37111-69508-Tim-Cook-Onstage-xl.jpg", 26 | ), 27 | UserModel( 28 | userId: "1", 29 | userName: "Ricky", 30 | userImage: 31 | "https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&h=750&w=1260", 32 | ), 33 | ]; 34 | 35 | List userData2 = [ 36 | UserModel( 37 | userId: "4", 38 | userName: "Chris", 39 | userImage: 40 | "https://images.pexels.com/photos/697509/pexels-photo-697509.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 41 | ), 42 | UserModel( 43 | userId: "4", 44 | userName: "Shorr", 45 | userImage: 46 | "https://images.pexels.com/photos/1310522/pexels-photo-1310522.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 47 | ), 48 | ]; 49 | } 50 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ClubHouseClone 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/share/icon_add.dart: -------------------------------------------------------------------------------- 1 | import 'package:ClubHouseClone/share/shared_style.dart'; 2 | import 'package:cached_network_image/cached_network_image.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class IconAdd extends StatelessWidget { 7 | final String imageUrl; 8 | final double size; 9 | const IconAdd({ 10 | Key key, 11 | @required this.imageUrl, 12 | this.size = 48, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | child: Stack( 19 | children: [ 20 | Padding( 21 | padding: EdgeInsets.all(5), 22 | child: Container( 23 | height: size * 1.05, 24 | width: size, 25 | decoration: BoxDecoration( 26 | borderRadius: BorderRadius.circular(size / 2.1), 27 | ), 28 | clipBehavior: Clip.hardEdge, 29 | child: CachedNetworkImage( 30 | imageUrl: imageUrl, 31 | fit: BoxFit.cover, 32 | ), 33 | ), 34 | ), 35 | Positioned( 36 | left: size / 2.5 > 18 ? 0 : 2, 37 | bottom: size / 2.5 > 18 ? 0 : 2, 38 | child: Container( 39 | alignment: Alignment.center, 40 | height: size / 2.5, 41 | width: size / 2.5, 42 | decoration: BoxDecoration( 43 | shape: BoxShape.circle, 44 | color: themeColor, 45 | ), 46 | child: Icon(Icons.add_circle_rounded, 47 | color: indigo, size: size / 2.5), 48 | ), 49 | ), 50 | ], 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /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 29 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.ClubHouseClone" 42 | minSdkVersion 16 43 | targetSdkVersion 29 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /lib/share/snapping_scroll_physics.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | class SnappingScrollPhysics extends ScrollPhysics { 7 | final double mainAxisStartPadding; 8 | final double itemExtent; 9 | 10 | const SnappingScrollPhysics({ 11 | ScrollPhysics parent, 12 | this.mainAxisStartPadding = 0.0, 13 | @required this.itemExtent, 14 | }) : super(parent: parent); 15 | 16 | @override 17 | SnappingScrollPhysics applyTo(ScrollPhysics ancestor) { 18 | return SnappingScrollPhysics( 19 | parent: buildParent(ancestor), 20 | mainAxisStartPadding: mainAxisStartPadding, 21 | itemExtent: itemExtent, 22 | ); 23 | } 24 | 25 | double _getItem(ScrollPosition position) { 26 | return (position.pixels - mainAxisStartPadding) / itemExtent; 27 | } 28 | 29 | double _getPixels(ScrollPosition position, double item) { 30 | return min(item * itemExtent, position.maxScrollExtent); 31 | } 32 | 33 | double _getTargetPixels( 34 | ScrollPosition position, 35 | Tolerance tolerance, 36 | double velocity, 37 | ) { 38 | double item = _getItem(position); 39 | if (velocity < -tolerance.velocity) { 40 | item -= 0.5; 41 | } else if (velocity > tolerance.velocity) { 42 | item += 0.5; 43 | } 44 | return _getPixels(position, item.roundToDouble()); 45 | } 46 | 47 | @override 48 | Simulation createBallisticSimulation( 49 | ScrollMetrics position, 50 | double velocity, 51 | ) { 52 | if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) || 53 | (velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) 54 | return super.createBallisticSimulation(position, velocity); 55 | final Tolerance tolerance = this.tolerance; 56 | final double target = _getTargetPixels(position, tolerance, velocity); 57 | if (target != position.pixels) 58 | return ScrollSpringSimulation(spring, position.pixels, target, velocity, 59 | tolerance: tolerance); 60 | return null; 61 | } 62 | 63 | @override 64 | bool get allowImplicitScrolling => false; 65 | } 66 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /lib/share/shared_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const Widget horizontalSpaceTiny = SizedBox(width: 5.0); 4 | const Widget horizontalSpaceSmall = SizedBox(width: 10.0); 5 | const Widget horizontalSpaceMedium = SizedBox(width: 25.0); 6 | 7 | const Widget verticalSpaceTiny = SizedBox(height: 5.0); 8 | const Widget verticalSpaceSmall = SizedBox(height: 10.0); 9 | const Widget verticalSpaceMedium = SizedBox(height: 25.0); 10 | const Widget verticalSpaceLarge = SizedBox(height: 50.0); 11 | const Widget verticalSpaceMassive = SizedBox(height: 120.0); 12 | 13 | Widget spacedDivider = Column( 14 | children: const [ 15 | verticalSpaceMedium, 16 | const Divider(color: Colors.blueGrey, height: 5.0), 17 | verticalSpaceMedium, 18 | ], 19 | ); 20 | 21 | Widget verticalSpace(double height) => SizedBox(height: height); 22 | 23 | double screenWidth(BuildContext context) => MediaQuery.of(context).size.width; 24 | double screenHeight(BuildContext context) => MediaQuery.of(context).size.height; 25 | 26 | double screenHeightFraction(BuildContext context, 27 | {int dividedBy = 1, double offsetBy = 0}) => 28 | (screenHeight(context) - offsetBy) / dividedBy; 29 | 30 | double screenWidthFraction(BuildContext context, 31 | {int dividedBy = 1, double offsetBy = 0}) => 32 | (screenWidth(context) - offsetBy) / dividedBy; 33 | 34 | double halfScreenWidth(BuildContext context) => 35 | screenWidthFraction(context, dividedBy: 2); 36 | 37 | double thirdScreenWidth(BuildContext context) => 38 | screenWidthFraction(context, dividedBy: 3); 39 | 40 | // Box Decorations 41 | 42 | BoxDecoration fieldDecortaion = 43 | BoxDecoration(borderRadius: BorderRadius.circular(40), color: Colors.white); 44 | 45 | BoxDecoration disabledFieldDecortaion = BoxDecoration( 46 | borderRadius: BorderRadius.circular(40), color: Colors.grey[100]); 47 | 48 | // Field Variables 49 | 50 | const double fieldHeight = 55; 51 | const double smallFieldHeight = 40; 52 | const double inputFieldBottomMargin = 30; 53 | const double inputFieldSmallBottomMargin = 0; 54 | const EdgeInsets fieldPadding = const EdgeInsets.symmetric(horizontal: 15); 55 | const EdgeInsets largeFieldPadding = 56 | const EdgeInsets.symmetric(horizontal: 15, vertical: 15); 57 | 58 | // Text Variables 59 | const TextStyle buttonTitleTextStyle = 60 | const TextStyle(fontWeight: FontWeight.w700, color: Colors.white); 61 | 62 | // Colors 63 | const Color themeColor = Color(0xFFf1eee5); 64 | const Color followColor = Color(0xFFf7f4ef); 65 | const Color indigo = Color(0xFF5c75a6); 66 | const Color green = Color(0xFF56ab68); 67 | const Color brown = Color(0xFF6f664e); 68 | const Color search = Color(0xFFe3e0d9); 69 | const Color white = Color(0xFFffffff); 70 | const Color black = Color(0xFF000000); 71 | const Color grey = Color(0xFFf2f2f2); 72 | const Color grey1 = Color(0xFFdddddd); 73 | 74 | const Color grey2 = Color(0xFFe8e7ec); 75 | const Color grey3 = Color(0xFFf9f8fb); 76 | 77 | const double fontSize1 = 15; 78 | const double fontSize2 = 16; 79 | const double fontSize3 = 20; 80 | const double fontSize4 = 23; 81 | const double fontSize5 = 26; 82 | const double fontSize6 = 39; 83 | -------------------------------------------------------------------------------- /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.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: ClubHouseClone 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.7.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^1.0.0 31 | get_it: ^3.0.0+1 32 | cached_network_image: ^2.5.1 33 | flutter_emoji: ">= 2.0.0" 34 | visibility_detector: ^0.1.5 35 | 36 | 37 | 38 | 39 | 40 | 41 | dev_dependencies: 42 | flutter_test: 43 | sdk: flutter 44 | 45 | # For information on the generic Dart part of this file, see the 46 | # following page: https://dart.dev/tools/pub/pubspec 47 | 48 | # The following section is specific to Flutter. 49 | flutter: 50 | 51 | # The following line ensures that the Material Icons font is 52 | # included with your application, so that you can use the icons in 53 | # the material Icons class. 54 | uses-material-design: true 55 | 56 | # To add assets to your application, add an assets section, like this: 57 | # assets: 58 | # - images/a_dot_burr.jpeg 59 | # - images/a_dot_ham.jpeg 60 | 61 | # An image asset can refer to one or more resolution-specific "variants", see 62 | # https://flutter.dev/assets-and-images/#resolution-aware. 63 | 64 | # For details regarding adding assets from package dependencies, see 65 | # https://flutter.dev/assets-and-images/#from-packages 66 | 67 | # To add custom fonts to your application, add a fonts section here, 68 | # in this "flutter" section. Each entry in this list should have a 69 | # "family" key with the font family name, and a "fonts" key with a 70 | # list giving the asset and other descriptors for the font. For 71 | # example: 72 | # fonts: 73 | # - family: Schyler 74 | # fonts: 75 | # - asset: fonts/Schyler-Regular.ttf 76 | # - asset: fonts/Schyler-Italic.ttf 77 | # style: italic 78 | # - family: Trajan Pro 79 | # fonts: 80 | # - asset: fonts/TrajanPro.ttf 81 | # - asset: fonts/TrajanPro_Bold.ttf 82 | # weight: 700 83 | # 84 | fonts: 85 | - family: Nunito 86 | fonts: 87 | - asset: fonts/Nunito-Black.ttf 88 | weight: 900 89 | - asset: fonts/Nunito-ExtraBold.ttf 90 | weight: 800 91 | - asset: fonts/Nunito-Bold.ttf 92 | weight: 700 93 | - asset: fonts/Nunito-SemiBold.ttf 94 | weight: 600 95 | - asset: fonts/Nunito-Regular.ttf 96 | weight: 400 97 | - asset: fonts/Nunito-Italic.ttf 98 | # For details regarding fonts from package dependencies, 99 | # see https://flutter.dev/custom-fonts/#from-packages 100 | -------------------------------------------------------------------------------- /lib/screen/search_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:ClubHouseClone/app/locator.dart'; 2 | import 'package:ClubHouseClone/app/navigation_service.dart'; 3 | import 'package:ClubHouseClone/share/my_sliver_appbar_delegate.dart'; 4 | import 'package:ClubHouseClone/share/shared_style.dart'; 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | import 'package:flutter/cupertino.dart'; 7 | import 'package:ClubHouseClone/share/nav_bar.dart' as nav_bar; 8 | import 'package:flutter/material.dart'; 9 | 10 | class SearchView extends StatefulWidget { 11 | const SearchView({ 12 | Key key, 13 | }) : super(key: key); 14 | 15 | @override 16 | State createState() { 17 | return _SearchViewState(); 18 | } 19 | } 20 | 21 | class _SearchViewState extends State with WidgetsBindingObserver { 22 | NavigationService _navigationService = locator(); 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | } 28 | 29 | @override 30 | void dispose() { 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | void didChangeAppLifecycleState(AppLifecycleState state) { 36 | print('state = $state'); 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return CupertinoPageScaffold( 42 | backgroundColor: themeColor, 43 | navigationBar: nav_bar.CupertinoNavigationBar( 44 | border: null, 45 | padding: EdgeInsetsDirectional.zero, 46 | backgroundColor: themeColor, 47 | automaticallyImplyLeading: false, 48 | leading: CupertinoButton( 49 | padding: EdgeInsets.zero, 50 | child: Icon( 51 | CupertinoIcons.back, 52 | size: 35, 53 | color: CupertinoColors.black, 54 | ), 55 | onPressed: () { 56 | _navigationService.pop(); 57 | }, 58 | ), 59 | middle: Text( 60 | "EXPLORE", 61 | textScaleFactor: 1.0, 62 | style: TextStyle(fontSize: 16, color: black), 63 | ), 64 | ), 65 | child: CustomScrollView( 66 | slivers: [ 67 | SliverPersistentHeader( 68 | pinned: true, 69 | delegate: MySliverAppBarDelegate( 70 | child: PreferredSize( 71 | preferredSize: Size.fromHeight(50.0), 72 | child: _searchWidget(), 73 | ), 74 | ), 75 | ), 76 | SliverToBoxAdapter( 77 | child: Container( 78 | padding: EdgeInsets.fromLTRB(16, 30, 16, 10), 79 | child: Text( 80 | "PEOPLE TO FELLOW", 81 | textScaleFactor: 1, 82 | style: TextStyle( 83 | fontWeight: FontWeight.bold, 84 | ), 85 | ), 86 | ), 87 | ), 88 | SliverList( 89 | delegate: SliverChildBuilderDelegate( 90 | (BuildContext context, int index) { 91 | return Container( 92 | height: 80, 93 | color: followColor, 94 | padding: EdgeInsets.symmetric(horizontal: 16), 95 | child: Row( 96 | children: [ 97 | Container( 98 | height: 45, 99 | width: 45, 100 | decoration: BoxDecoration( 101 | borderRadius: BorderRadius.circular(20), 102 | ), 103 | clipBehavior: Clip.hardEdge, 104 | child: CachedNetworkImage( 105 | imageUrl: 106 | "https://content.fortune.com/wp-content/uploads/2021/02/GettyImages-1229901940.jpg", 107 | fit: BoxFit.cover, 108 | ), 109 | ), 110 | SizedBox( 111 | width: 16, 112 | ), 113 | Expanded( 114 | child: Column( 115 | mainAxisAlignment: MainAxisAlignment.center, 116 | crossAxisAlignment: CrossAxisAlignment.start, 117 | children: [ 118 | Text( 119 | "Elon Mask", 120 | textScaleFactor: 1, 121 | style: TextStyle( 122 | fontSize: 16, fontWeight: FontWeight.bold), 123 | ), 124 | Text( 125 | "Lorem ipsum dolor sit amet, est eius accusata maluisset eu, omnis aperiam accusamus sit ex.", 126 | textScaleFactor: 1, 127 | maxLines: 2, 128 | overflow: TextOverflow.ellipsis, 129 | ), 130 | ], 131 | ), 132 | ), 133 | SizedBox( 134 | width: 16, 135 | ), 136 | FlatButton( 137 | child: Text( 138 | "Follow", 139 | textScaleFactor: 1, 140 | style: TextStyle( 141 | color: indigo, 142 | ), 143 | ), 144 | shape: RoundedRectangleBorder( 145 | side: BorderSide( 146 | color: indigo, 147 | width: 2, 148 | style: BorderStyle.solid, 149 | ), 150 | borderRadius: BorderRadius.circular(20)), 151 | onPressed: () {}, 152 | ), 153 | ], 154 | ), 155 | ); 156 | }, 157 | childCount: 3, 158 | ), 159 | ), 160 | SliverToBoxAdapter( 161 | child: Container( 162 | padding: EdgeInsets.fromLTRB(16, 30, 16, 10), 163 | child: Text( 164 | "FIND CONVERSATIONS ABOUT...", 165 | textScaleFactor: 1, 166 | style: TextStyle( 167 | fontWeight: FontWeight.bold, 168 | ), 169 | ), 170 | ), 171 | ), 172 | ], 173 | ), 174 | ); 175 | } 176 | 177 | _searchWidget() { 178 | return Container( 179 | height: 50, 180 | color: themeColor, 181 | child: Container( 182 | padding: EdgeInsets.fromLTRB(20, 0, 20, 10), 183 | child: GestureDetector( 184 | onTap: () {}, 185 | child: AnimatedContainer( 186 | duration: Duration(milliseconds: 200), 187 | decoration: BoxDecoration( 188 | color: search, 189 | borderRadius: BorderRadius.circular(8.0), 190 | ), 191 | child: Row( 192 | mainAxisAlignment: MainAxisAlignment.start, 193 | children: [ 194 | SizedBox( 195 | width: 5, 196 | ), 197 | Icon( 198 | CupertinoIcons.search, 199 | color: black.withOpacity(0.5), 200 | size: 22, 201 | ), 202 | SizedBox( 203 | width: 5, 204 | ), 205 | Expanded( 206 | child: Container( 207 | alignment: Alignment.centerLeft, 208 | child: Text( 209 | "Find People and Clubs", 210 | maxLines: 2, 211 | style: TextStyle( 212 | color: black.withOpacity(0.5), 213 | ), 214 | ), 215 | ), 216 | ), 217 | ], 218 | ), 219 | ), 220 | ), 221 | ), 222 | ); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.0.13" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.6.0" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.5.0-nullsafety.1" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.1.0-nullsafety.1" 32 | cached_network_image: 33 | dependency: "direct main" 34 | description: 35 | name: cached_network_image 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.5.1" 39 | characters: 40 | dependency: transitive 41 | description: 42 | name: characters 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0-nullsafety.3" 46 | charcode: 47 | dependency: transitive 48 | description: 49 | name: charcode 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.2.0-nullsafety.1" 53 | clock: 54 | dependency: transitive 55 | description: 56 | name: clock 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0-nullsafety.1" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.15.0-nullsafety.3" 67 | convert: 68 | dependency: transitive 69 | description: 70 | name: convert 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "2.1.1" 74 | crypto: 75 | dependency: transitive 76 | description: 77 | name: crypto 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.1.5" 81 | cupertino_icons: 82 | dependency: "direct main" 83 | description: 84 | name: cupertino_icons 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.0.0" 88 | fake_async: 89 | dependency: transitive 90 | description: 91 | name: fake_async 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.2.0-nullsafety.1" 95 | ffi: 96 | dependency: transitive 97 | description: 98 | name: ffi 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "0.1.3" 102 | file: 103 | dependency: transitive 104 | description: 105 | name: file 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "5.2.1" 109 | flutter: 110 | dependency: "direct main" 111 | description: flutter 112 | source: sdk 113 | version: "0.0.0" 114 | flutter_blurhash: 115 | dependency: transitive 116 | description: 117 | name: flutter_blurhash 118 | url: "https://pub.dartlang.org" 119 | source: hosted 120 | version: "0.5.0" 121 | flutter_cache_manager: 122 | dependency: transitive 123 | description: 124 | name: flutter_cache_manager 125 | url: "https://pub.dartlang.org" 126 | source: hosted 127 | version: "2.1.2" 128 | flutter_emoji: 129 | dependency: "direct main" 130 | description: 131 | name: flutter_emoji 132 | url: "https://pub.dartlang.org" 133 | source: hosted 134 | version: "2.2.1+1" 135 | flutter_test: 136 | dependency: "direct dev" 137 | description: flutter 138 | source: sdk 139 | version: "0.0.0" 140 | get_it: 141 | dependency: "direct main" 142 | description: 143 | name: get_it 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "3.1.0" 147 | http: 148 | dependency: transitive 149 | description: 150 | name: http 151 | url: "https://pub.dartlang.org" 152 | source: hosted 153 | version: "0.12.2" 154 | http_parser: 155 | dependency: transitive 156 | description: 157 | name: http_parser 158 | url: "https://pub.dartlang.org" 159 | source: hosted 160 | version: "3.1.4" 161 | image: 162 | dependency: transitive 163 | description: 164 | name: image 165 | url: "https://pub.dartlang.org" 166 | source: hosted 167 | version: "2.1.19" 168 | intl: 169 | dependency: transitive 170 | description: 171 | name: intl 172 | url: "https://pub.dartlang.org" 173 | source: hosted 174 | version: "0.16.1" 175 | matcher: 176 | dependency: transitive 177 | description: 178 | name: matcher 179 | url: "https://pub.dartlang.org" 180 | source: hosted 181 | version: "0.12.10-nullsafety.1" 182 | meta: 183 | dependency: transitive 184 | description: 185 | name: meta 186 | url: "https://pub.dartlang.org" 187 | source: hosted 188 | version: "1.3.0-nullsafety.3" 189 | octo_image: 190 | dependency: transitive 191 | description: 192 | name: octo_image 193 | url: "https://pub.dartlang.org" 194 | source: hosted 195 | version: "0.3.0" 196 | path: 197 | dependency: transitive 198 | description: 199 | name: path 200 | url: "https://pub.dartlang.org" 201 | source: hosted 202 | version: "1.8.0-nullsafety.1" 203 | path_provider: 204 | dependency: transitive 205 | description: 206 | name: path_provider 207 | url: "https://pub.dartlang.org" 208 | source: hosted 209 | version: "1.6.27" 210 | path_provider_linux: 211 | dependency: transitive 212 | description: 213 | name: path_provider_linux 214 | url: "https://pub.dartlang.org" 215 | source: hosted 216 | version: "0.0.1+2" 217 | path_provider_macos: 218 | dependency: transitive 219 | description: 220 | name: path_provider_macos 221 | url: "https://pub.dartlang.org" 222 | source: hosted 223 | version: "0.0.4+8" 224 | path_provider_platform_interface: 225 | dependency: transitive 226 | description: 227 | name: path_provider_platform_interface 228 | url: "https://pub.dartlang.org" 229 | source: hosted 230 | version: "1.0.4" 231 | path_provider_windows: 232 | dependency: transitive 233 | description: 234 | name: path_provider_windows 235 | url: "https://pub.dartlang.org" 236 | source: hosted 237 | version: "0.0.4+3" 238 | pedantic: 239 | dependency: transitive 240 | description: 241 | name: pedantic 242 | url: "https://pub.dartlang.org" 243 | source: hosted 244 | version: "1.9.2" 245 | petitparser: 246 | dependency: transitive 247 | description: 248 | name: petitparser 249 | url: "https://pub.dartlang.org" 250 | source: hosted 251 | version: "3.1.0" 252 | platform: 253 | dependency: transitive 254 | description: 255 | name: platform 256 | url: "https://pub.dartlang.org" 257 | source: hosted 258 | version: "2.2.1" 259 | plugin_platform_interface: 260 | dependency: transitive 261 | description: 262 | name: plugin_platform_interface 263 | url: "https://pub.dartlang.org" 264 | source: hosted 265 | version: "1.0.3" 266 | process: 267 | dependency: transitive 268 | description: 269 | name: process 270 | url: "https://pub.dartlang.org" 271 | source: hosted 272 | version: "3.0.13" 273 | rxdart: 274 | dependency: transitive 275 | description: 276 | name: rxdart 277 | url: "https://pub.dartlang.org" 278 | source: hosted 279 | version: "0.25.0" 280 | sky_engine: 281 | dependency: transitive 282 | description: flutter 283 | source: sdk 284 | version: "0.0.99" 285 | source_span: 286 | dependency: transitive 287 | description: 288 | name: source_span 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "1.8.0-nullsafety.2" 292 | sqflite: 293 | dependency: transitive 294 | description: 295 | name: sqflite 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "1.3.2+4" 299 | sqflite_common: 300 | dependency: transitive 301 | description: 302 | name: sqflite_common 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "1.0.3+3" 306 | stack_trace: 307 | dependency: transitive 308 | description: 309 | name: stack_trace 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "1.10.0-nullsafety.1" 313 | stream_channel: 314 | dependency: transitive 315 | description: 316 | name: stream_channel 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "2.1.0-nullsafety.1" 320 | string_scanner: 321 | dependency: transitive 322 | description: 323 | name: string_scanner 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "1.1.0-nullsafety.1" 327 | synchronized: 328 | dependency: transitive 329 | description: 330 | name: synchronized 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "2.2.0+2" 334 | term_glyph: 335 | dependency: transitive 336 | description: 337 | name: term_glyph 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "1.2.0-nullsafety.1" 341 | test_api: 342 | dependency: transitive 343 | description: 344 | name: test_api 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "0.2.19-nullsafety.2" 348 | typed_data: 349 | dependency: transitive 350 | description: 351 | name: typed_data 352 | url: "https://pub.dartlang.org" 353 | source: hosted 354 | version: "1.3.0-nullsafety.3" 355 | uuid: 356 | dependency: transitive 357 | description: 358 | name: uuid 359 | url: "https://pub.dartlang.org" 360 | source: hosted 361 | version: "2.2.2" 362 | vector_math: 363 | dependency: transitive 364 | description: 365 | name: vector_math 366 | url: "https://pub.dartlang.org" 367 | source: hosted 368 | version: "2.1.0-nullsafety.3" 369 | visibility_detector: 370 | dependency: "direct main" 371 | description: 372 | name: visibility_detector 373 | url: "https://pub.dartlang.org" 374 | source: hosted 375 | version: "0.1.5" 376 | win32: 377 | dependency: transitive 378 | description: 379 | name: win32 380 | url: "https://pub.dartlang.org" 381 | source: hosted 382 | version: "1.7.4+1" 383 | xdg_directories: 384 | dependency: transitive 385 | description: 386 | name: xdg_directories 387 | url: "https://pub.dartlang.org" 388 | source: hosted 389 | version: "0.1.2" 390 | xml: 391 | dependency: transitive 392 | description: 393 | name: xml 394 | url: "https://pub.dartlang.org" 395 | source: hosted 396 | version: "4.5.1" 397 | sdks: 398 | dart: ">=2.10.2 <2.11.0" 399 | flutter: ">=1.22.2 <2.0.0" 400 | -------------------------------------------------------------------------------- /lib/share/text.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // @dart = 2.8 6 | 7 | import 'dart:ui' as ui show TextHeightBehavior; 8 | 9 | import 'package:flutter/foundation.dart'; 10 | import 'package:flutter/material.dart'; 11 | import 'package:flutter/painting.dart'; 12 | 13 | // Examples can assume: 14 | // String _name; 15 | 16 | /// The text style to apply to descendant [Text] widgets which don't have an 17 | /// explicit style. 18 | /// 19 | /// See also: 20 | /// 21 | /// * [AnimatedDefaultTextStyle], which animates changes in the text style 22 | /// smoothly over a given duration. 23 | /// * [DefaultTextStyleTransition], which takes a provided [Animation] to 24 | /// animate changes in text style smoothly over time. 25 | class DefaultTextStyle extends InheritedTheme { 26 | /// Creates a default text style for the given subtree. 27 | /// 28 | /// Consider using [DefaultTextStyle.merge] to inherit styling information 29 | /// from the current default text style for a given [BuildContext]. 30 | /// 31 | /// The [style] and [child] arguments are required and must not be null. 32 | /// 33 | /// The [softWrap] and [overflow] arguments must not be null (though they do 34 | /// have default values). 35 | /// 36 | /// The [maxLines] property may be null (and indeed defaults to null), but if 37 | /// it is not null, it must be greater than zero. 38 | const DefaultTextStyle({ 39 | Key key, 40 | @required this.style, 41 | this.textAlign, 42 | this.softWrap = true, 43 | this.overflow = TextOverflow.clip, 44 | this.maxLines, 45 | this.textWidthBasis = TextWidthBasis.parent, 46 | this.textHeightBehavior, 47 | @required Widget child, 48 | }) : assert(style != null), 49 | assert(softWrap != null), 50 | assert(overflow != null), 51 | assert(maxLines == null || maxLines > 0), 52 | assert(child != null), 53 | assert(textWidthBasis != null), 54 | super(key: key, child: child); 55 | 56 | /// A const-constructable default text style that provides fallback values. 57 | /// 58 | /// Returned from [of] when the given [BuildContext] doesn't have an enclosing default text style. 59 | /// 60 | /// This constructor creates a [DefaultTextStyle] that lacks a [child], which 61 | /// means the constructed value cannot be incorporated into the tree. 62 | const DefaultTextStyle.fallback({ Key key }) 63 | : style = const TextStyle(), 64 | textAlign = null, 65 | softWrap = true, 66 | maxLines = null, 67 | overflow = TextOverflow.clip, 68 | textWidthBasis = TextWidthBasis.parent, 69 | textHeightBehavior = null, 70 | super(key: key, child: null); 71 | 72 | /// Creates a default text style that overrides the text styles in scope at 73 | /// this point in the widget tree. 74 | /// 75 | /// The given [style] is merged with the [style] from the default text style 76 | /// for the [BuildContext] where the widget is inserted, and any of the other 77 | /// arguments that are not null replace the corresponding properties on that 78 | /// same default text style. 79 | /// 80 | /// This constructor cannot be used to override the [maxLines] property of the 81 | /// ancestor with the value null, since null here is used to mean "defer to 82 | /// ancestor". To replace a non-null [maxLines] from an ancestor with the null 83 | /// value (to remove the restriction on number of lines), manually obtain the 84 | /// ambient [DefaultTextStyle] using [DefaultTextStyle.of], then create a new 85 | /// [DefaultTextStyle] using the [new DefaultTextStyle] constructor directly. 86 | /// See the source below for an example of how to do this (since that's 87 | /// essentially what this constructor does). 88 | static Widget merge({ 89 | Key key, 90 | TextStyle style, 91 | TextAlign textAlign, 92 | bool softWrap, 93 | TextOverflow overflow, 94 | int maxLines, 95 | TextWidthBasis textWidthBasis, 96 | @required Widget child, 97 | }) { 98 | assert(child != null); 99 | return Builder( 100 | builder: (BuildContext context) { 101 | final DefaultTextStyle parent = DefaultTextStyle.of(context); 102 | return DefaultTextStyle( 103 | key: key, 104 | style: parent.style.merge(style), 105 | textAlign: textAlign ?? parent.textAlign, 106 | softWrap: softWrap ?? parent.softWrap, 107 | overflow: overflow ?? parent.overflow, 108 | maxLines: maxLines ?? parent.maxLines, 109 | textWidthBasis: textWidthBasis ?? parent.textWidthBasis, 110 | child: child, 111 | ); 112 | }, 113 | ); 114 | } 115 | 116 | /// The text style to apply. 117 | final TextStyle style; 118 | 119 | /// How each line of text in the Text widget should be aligned horizontally. 120 | final TextAlign textAlign; 121 | 122 | /// Whether the text should break at soft line breaks. 123 | /// 124 | /// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space. 125 | /// 126 | /// This also decides the [overflow] property's behavior. If this is true or null, 127 | /// the glyph causing overflow, and those that follow, will not be rendered. 128 | final bool softWrap; 129 | 130 | /// How visual overflow should be handled. 131 | /// 132 | /// If [softWrap] is true or null, the glyph causing overflow, and those that follow, 133 | /// will not be rendered. Otherwise, it will be shown with the given overflow option. 134 | final TextOverflow overflow; 135 | 136 | /// An optional maximum number of lines for the text to span, wrapping if necessary. 137 | /// If the text exceeds the given number of lines, it will be truncated according 138 | /// to [overflow]. 139 | /// 140 | /// If this is 1, text will not wrap. Otherwise, text will be wrapped at the 141 | /// edge of the box. 142 | /// 143 | /// If this is non-null, it will override even explicit null values of 144 | /// [Text.maxLines]. 145 | final int maxLines; 146 | 147 | /// The strategy to use when calculating the width of the Text. 148 | /// 149 | /// See [TextWidthBasis] for possible values and their implications. 150 | final TextWidthBasis textWidthBasis; 151 | 152 | /// {@macro flutter.dart:ui.textHeightBehavior} 153 | final ui.TextHeightBehavior textHeightBehavior; 154 | 155 | /// The closest instance of this class that encloses the given context. 156 | /// 157 | /// If no such instance exists, returns an instance created by 158 | /// [DefaultTextStyle.fallback], which contains fallback values. 159 | /// 160 | /// Typical usage is as follows: 161 | /// 162 | /// ```dart 163 | /// DefaultTextStyle style = DefaultTextStyle.of(context); 164 | /// ``` 165 | static DefaultTextStyle of(BuildContext context) { 166 | return context.dependOnInheritedWidgetOfExactType() ?? const DefaultTextStyle.fallback(); 167 | } 168 | 169 | @override 170 | bool updateShouldNotify(DefaultTextStyle oldWidget) { 171 | return style != oldWidget.style || 172 | textAlign != oldWidget.textAlign || 173 | softWrap != oldWidget.softWrap || 174 | overflow != oldWidget.overflow || 175 | maxLines != oldWidget.maxLines || 176 | textWidthBasis != oldWidget.textWidthBasis || 177 | textHeightBehavior != oldWidget.textHeightBehavior; 178 | } 179 | 180 | @override 181 | Widget wrap(BuildContext context, Widget child) { 182 | final DefaultTextStyle defaultTextStyle = context.findAncestorWidgetOfExactType(); 183 | return identical(this, defaultTextStyle) ? child : DefaultTextStyle( 184 | style: style, 185 | textAlign: textAlign, 186 | softWrap: softWrap, 187 | overflow: overflow, 188 | maxLines: maxLines, 189 | textWidthBasis: textWidthBasis, 190 | textHeightBehavior: textHeightBehavior, 191 | child: child, 192 | ); 193 | } 194 | 195 | @override 196 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 197 | super.debugFillProperties(properties); 198 | style?.debugFillProperties(properties); 199 | properties.add(EnumProperty('textAlign', textAlign, defaultValue: null)); 200 | properties.add(FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true)); 201 | properties.add(EnumProperty('overflow', overflow, defaultValue: null)); 202 | properties.add(IntProperty('maxLines', maxLines, defaultValue: null)); 203 | properties.add(EnumProperty('textWidthBasis', textWidthBasis, defaultValue: TextWidthBasis.parent)); 204 | properties.add(DiagnosticsProperty('textHeightBehavior', textHeightBehavior, defaultValue: null)); 205 | } 206 | } 207 | 208 | /// The [TextHeightBehavior] that will apply to descendant [Text] and [EditableText] 209 | /// widgets which have not explicitly set [Text.textHeightBehavior]. 210 | /// 211 | /// If there is a [DefaultTextStyle] with a non-null [DefaultTextStyle.textHeightBehavior] 212 | /// below this widget, the [DefaultTextStyle.textHeightBehavior] will be used 213 | /// over this widget's [TextHeightBehavior]. 214 | /// 215 | /// See also: 216 | /// 217 | /// * [DefaultTextStyle], which defines a [TextStyle] to apply to descendant 218 | /// [Text] widgets. 219 | class DefaultTextHeightBehavior extends InheritedTheme { 220 | /// Creates a default text height behavior for the given subtree. 221 | /// 222 | /// The [textHeightBehavior] and [child] arguments are required and must not be null. 223 | const DefaultTextHeightBehavior({ 224 | Key key, 225 | @required this.textHeightBehavior, 226 | @required Widget child, 227 | }) : assert(textHeightBehavior != null), 228 | assert(child != null), 229 | super(key: key, child: child); 230 | 231 | /// {@macro flutter.dart:ui.textHeightBehavior} 232 | final TextHeightBehavior textHeightBehavior; 233 | 234 | /// The closest instance of this class that encloses the given context. 235 | /// 236 | /// If no such instance exists, this method will return `null`. 237 | /// 238 | /// Typical usage is as follows: 239 | /// 240 | /// ```dart 241 | /// DefaultTextHeightBehavior defaultTextHeightBehavior = DefaultTextHeightBehavior.of(context); 242 | /// ``` 243 | static TextHeightBehavior of(BuildContext context) { 244 | return context.dependOnInheritedWidgetOfExactType()?.textHeightBehavior; 245 | } 246 | 247 | @override 248 | bool updateShouldNotify(DefaultTextHeightBehavior oldWidget) { 249 | return textHeightBehavior != oldWidget.textHeightBehavior; 250 | } 251 | 252 | @override 253 | Widget wrap(BuildContext context, Widget child) { 254 | final DefaultTextHeightBehavior defaultTextHeightBehavior = context.findAncestorWidgetOfExactType(); 255 | return identical(this, defaultTextHeightBehavior) ? child : DefaultTextHeightBehavior( 256 | textHeightBehavior: textHeightBehavior, 257 | child: child, 258 | ); 259 | } 260 | 261 | @override 262 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 263 | super.debugFillProperties(properties); 264 | properties.add(DiagnosticsProperty('textHeightBehavior', textHeightBehavior, defaultValue: null)); 265 | } 266 | } 267 | 268 | /// A run of text with a single style. 269 | /// 270 | /// The [Text] widget displays a string of text with single style. The string 271 | /// might break across multiple lines or might all be displayed on the same line 272 | /// depending on the layout constraints. 273 | /// 274 | /// The [style] argument is optional. When omitted, the text will use the style 275 | /// from the closest enclosing [DefaultTextStyle]. If the given style's 276 | /// [TextStyle.inherit] property is true (the default), the given style will 277 | /// be merged with the closest enclosing [DefaultTextStyle]. This merging 278 | /// behavior is useful, for example, to make the text bold while using the 279 | /// default font family and size. 280 | /// 281 | /// {@tool snippet} 282 | /// 283 | /// This example shows how to display text using the [Text] widget with the 284 | /// [overflow] set to [TextOverflow.ellipsis]. 285 | /// 286 | /// ![If the text is shorter than the available space, it is displayed in full without an ellipsis.](https://flutter.github.io/assets-for-api-docs/assets/widgets/text.png) 287 | /// 288 | /// ![If the text overflows, the Text widget displays an ellipsis to trim the overflowing text](https://flutter.github.io/assets-for-api-docs/assets/widgets/text_ellipsis.png) 289 | /// 290 | /// ```dart 291 | /// Text( 292 | /// 'Hello, $_name! How are you?', 293 | /// textAlign: TextAlign.center, 294 | /// overflow: TextOverflow.ellipsis, 295 | /// style: TextStyle(fontWeight: FontWeight.bold), 296 | /// ) 297 | /// ``` 298 | /// {@end-tool} 299 | /// 300 | /// Using the [Text.rich] constructor, the [Text] widget can 301 | /// display a paragraph with differently styled [TextSpan]s. The sample 302 | /// that follows displays "Hello beautiful world" with different styles 303 | /// for each word. 304 | /// 305 | /// {@tool snippet} 306 | /// 307 | /// ![The word "Hello" is shown with the default text styles. The word "beautiful" is italicized. The word "world" is bold.](https://flutter.github.io/assets-for-api-docs/assets/widgets/text_rich.png) 308 | /// 309 | /// ```dart 310 | /// const Text.rich( 311 | /// TextSpan( 312 | /// text: 'Hello', // default text style 313 | /// children: [ 314 | /// TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)), 315 | /// TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)), 316 | /// ], 317 | /// ), 318 | /// ) 319 | /// ``` 320 | /// {@end-tool} 321 | /// 322 | /// ## Interactivity 323 | /// 324 | /// To make [Text] react to touch events, wrap it in a [GestureDetector] widget 325 | /// with a [GestureDetector.onTap] handler. 326 | /// 327 | /// In a material design application, consider using a [TextButton] instead, or 328 | /// if that isn't appropriate, at least using an [InkWell] instead of 329 | /// [GestureDetector]. 330 | /// 331 | /// To make sections of the text interactive, use [RichText] and specify a 332 | /// [TapGestureRecognizer] as the [TextSpan.recognizer] of the relevant part of 333 | /// the text. 334 | /// 335 | /// See also: 336 | /// 337 | /// * [RichText], which gives you more control over the text styles. 338 | /// * [DefaultTextStyle], which sets default styles for [Text] widgets. 339 | class Text extends StatelessWidget { 340 | /// Creates a text widget. 341 | /// 342 | /// If the [style] argument is null, the text will use the style from the 343 | /// closest enclosing [DefaultTextStyle]. 344 | /// 345 | /// The [data] parameter must not be null. 346 | /// 347 | /// The [overflow] property's behavior is affected by the [softWrap] argument. 348 | /// If the [softWrap] is true or null, the glyph causing overflow, and those that follow, 349 | /// will not be rendered. Otherwise, it will be shown with the given overflow option. 350 | const Text( 351 | this.data, { 352 | Key key, 353 | this.style, 354 | this.strutStyle, 355 | this.textAlign, 356 | this.textDirection, 357 | this.locale, 358 | this.softWrap, 359 | this.overflow, 360 | this.textScaleFactor, 361 | this.maxLines, 362 | this.semanticsLabel, 363 | this.textWidthBasis, 364 | this.textHeightBehavior, 365 | }) : assert( 366 | data != null, 367 | 'A non-null String must be provided to a Text widget.', 368 | ), 369 | textSpan = null, 370 | super(key: key); 371 | 372 | /// Creates a text widget with a [InlineSpan]. 373 | /// 374 | /// The following subclasses of [InlineSpan] may be used to build rich text: 375 | /// 376 | /// * [TextSpan]s define text and children [InlineSpan]s. 377 | /// * [WidgetSpan]s define embedded inline widgets. 378 | /// 379 | /// The [textSpan] parameter must not be null. 380 | /// 381 | /// See [RichText] which provides a lower-level way to draw text. 382 | const Text.rich( 383 | this.textSpan, { 384 | Key key, 385 | this.style, 386 | this.strutStyle, 387 | this.textAlign, 388 | this.textDirection, 389 | this.locale, 390 | this.softWrap, 391 | this.overflow, 392 | this.textScaleFactor, 393 | this.maxLines, 394 | this.semanticsLabel, 395 | this.textWidthBasis, 396 | this.textHeightBehavior, 397 | }) : assert( 398 | textSpan != null, 399 | 'A non-null TextSpan must be provided to a Text.rich widget.', 400 | ), 401 | data = null, 402 | super(key: key); 403 | 404 | /// The text to display. 405 | /// 406 | /// This will be null if a [textSpan] is provided instead. 407 | final String data; 408 | 409 | /// The text to display as a [InlineSpan]. 410 | /// 411 | /// This will be null if [data] is provided instead. 412 | final InlineSpan textSpan; 413 | 414 | /// If non-null, the style to use for this text. 415 | /// 416 | /// If the style's "inherit" property is true, the style will be merged with 417 | /// the closest enclosing [DefaultTextStyle]. Otherwise, the style will 418 | /// replace the closest enclosing [DefaultTextStyle]. 419 | final TextStyle style; 420 | 421 | /// {@macro flutter.painting.textPainter.strutStyle} 422 | final StrutStyle strutStyle; 423 | 424 | /// How the text should be aligned horizontally. 425 | final TextAlign textAlign; 426 | 427 | /// The directionality of the text. 428 | /// 429 | /// This decides how [textAlign] values like [TextAlign.start] and 430 | /// [TextAlign.end] are interpreted. 431 | /// 432 | /// This is also used to disambiguate how to render bidirectional text. For 433 | /// example, if the [data] is an English phrase followed by a Hebrew phrase, 434 | /// in a [TextDirection.ltr] context the English phrase will be on the left 435 | /// and the Hebrew phrase to its right, while in a [TextDirection.rtl] 436 | /// context, the English phrase will be on the right and the Hebrew phrase on 437 | /// its left. 438 | /// 439 | /// Defaults to the ambient [Directionality], if any. 440 | final TextDirection textDirection; 441 | 442 | /// Used to select a font when the same Unicode character can 443 | /// be rendered differently, depending on the locale. 444 | /// 445 | /// It's rarely necessary to set this property. By default its value 446 | /// is inherited from the enclosing app with `Localizations.localeOf(context)`. 447 | /// 448 | /// See [RenderParagraph.locale] for more information. 449 | final Locale locale; 450 | 451 | /// Whether the text should break at soft line breaks. 452 | /// 453 | /// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space. 454 | final bool softWrap; 455 | 456 | /// How visual overflow should be handled. 457 | /// 458 | /// Defaults to retrieving the value from the nearest [DefaultTextStyle] ancestor. 459 | final TextOverflow overflow; 460 | 461 | /// The number of font pixels for each logical pixel. 462 | /// 463 | /// For example, if the text scale factor is 1.5, text will be 50% larger than 464 | /// the specified font size. 465 | /// 466 | /// The value given to the constructor as textScaleFactor. If null, will 467 | /// use the [MediaQueryData.textScaleFactor] obtained from the ambient 468 | /// [MediaQuery], or 1.0 if there is no [MediaQuery] in scope. 469 | final double textScaleFactor; 470 | 471 | /// An optional maximum number of lines for the text to span, wrapping if necessary. 472 | /// If the text exceeds the given number of lines, it will be truncated according 473 | /// to [overflow]. 474 | /// 475 | /// If this is 1, text will not wrap. Otherwise, text will be wrapped at the 476 | /// edge of the box. 477 | /// 478 | /// If this is null, but there is an ambient [DefaultTextStyle] that specifies 479 | /// an explicit number for its [DefaultTextStyle.maxLines], then the 480 | /// [DefaultTextStyle] value will take precedence. You can use a [RichText] 481 | /// widget directly to entirely override the [DefaultTextStyle]. 482 | final int maxLines; 483 | 484 | /// An alternative semantics label for this text. 485 | /// 486 | /// If present, the semantics of this widget will contain this value instead 487 | /// of the actual text. This will overwrite any of the semantics labels applied 488 | /// directly to the [TextSpan]s. 489 | /// 490 | /// This is useful for replacing abbreviations or shorthands with the full 491 | /// text value: 492 | /// 493 | /// ```dart 494 | /// Text(r'$$', semanticsLabel: 'Double dollars') 495 | /// ``` 496 | final String semanticsLabel; 497 | 498 | /// {@macro flutter.painting.textPainter.textWidthBasis} 499 | final TextWidthBasis textWidthBasis; 500 | 501 | /// {@macro flutter.dart:ui.textHeightBehavior} 502 | final ui.TextHeightBehavior textHeightBehavior; 503 | 504 | @override 505 | Widget build(BuildContext context) { 506 | final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); 507 | TextStyle effectiveTextStyle = style; 508 | if (style == null || style.inherit) 509 | effectiveTextStyle = defaultTextStyle.style.merge(style); 510 | if (MediaQuery.boldTextOverride(context)) 511 | effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold)); 512 | Widget result = RichText( 513 | textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start, 514 | textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null. 515 | locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null 516 | softWrap: softWrap ?? defaultTextStyle.softWrap, 517 | overflow: overflow ?? defaultTextStyle.overflow, 518 | textScaleFactor: textScaleFactor ?? 1, 519 | maxLines: maxLines ?? defaultTextStyle.maxLines, 520 | strutStyle: strutStyle, 521 | textWidthBasis: textWidthBasis ?? defaultTextStyle.textWidthBasis, 522 | textHeightBehavior: textHeightBehavior ?? defaultTextStyle.textHeightBehavior ?? DefaultTextHeightBehavior.of(context), 523 | text: TextSpan( 524 | style: effectiveTextStyle, 525 | text: data, 526 | children: textSpan != null ? [textSpan] : null, 527 | ), 528 | ); 529 | if (semanticsLabel != null) { 530 | result = Semantics( 531 | textDirection: textDirection, 532 | label: semanticsLabel, 533 | child: ExcludeSemantics( 534 | child: result, 535 | ), 536 | ); 537 | } 538 | return result; 539 | } 540 | 541 | @override 542 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 543 | super.debugFillProperties(properties); 544 | properties.add(StringProperty('data', data, showName: false)); 545 | if (textSpan != null) { 546 | properties.add(textSpan.toDiagnosticsNode(name: 'textSpan', style: DiagnosticsTreeStyle.transition)); 547 | } 548 | style?.debugFillProperties(properties); 549 | properties.add(EnumProperty('textAlign', textAlign, defaultValue: null)); 550 | properties.add(EnumProperty('textDirection', textDirection, defaultValue: null)); 551 | properties.add(DiagnosticsProperty('locale', locale, defaultValue: null)); 552 | properties.add(FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true)); 553 | properties.add(EnumProperty('overflow', overflow, defaultValue: null)); 554 | properties.add(DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null)); 555 | properties.add(IntProperty('maxLines', maxLines, defaultValue: null)); 556 | properties.add(EnumProperty('textWidthBasis', textWidthBasis, defaultValue: null)); 557 | properties.add(DiagnosticsProperty('textHeightBehavior', textHeightBehavior, defaultValue: null)); 558 | if (semanticsLabel != null) { 559 | properties.add(StringProperty('semanticsLabel', semanticsLabel)); 560 | } 561 | } 562 | } 563 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 221A1473CB51FCB348029855 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EFF6A05ACAFA9E11C23EE9D /* Pods_Runner.framework */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 1EFF6A05ACAFA9E11C23EE9D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 2B029A4094A056E628E857C8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 37 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 38 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 39 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 40 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 41 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 42 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 43 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 45 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 47 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | D1A700314C3A9FC91CA54CE9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 49 | EE3DD467847E5C1633BA369A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 221A1473CB51FCB348029855 /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 3276DCCFAC90F5C55343B482 /* Pods */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 2B029A4094A056E628E857C8 /* Pods-Runner.debug.xcconfig */, 68 | D1A700314C3A9FC91CA54CE9 /* Pods-Runner.release.xcconfig */, 69 | EE3DD467847E5C1633BA369A /* Pods-Runner.profile.xcconfig */, 70 | ); 71 | path = Pods; 72 | sourceTree = ""; 73 | }; 74 | 3E8E85268CD00DFA9C8F7ED0 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 1EFF6A05ACAFA9E11C23EE9D /* Pods_Runner.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | 9740EEB11CF90186004384FC /* Flutter */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 86 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 87 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 88 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 89 | ); 90 | name = Flutter; 91 | sourceTree = ""; 92 | }; 93 | 97C146E51CF9000F007C117D = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9740EEB11CF90186004384FC /* Flutter */, 97 | 97C146F01CF9000F007C117D /* Runner */, 98 | 97C146EF1CF9000F007C117D /* Products */, 99 | 3276DCCFAC90F5C55343B482 /* Pods */, 100 | 3E8E85268CD00DFA9C8F7ED0 /* Frameworks */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | 97C146EF1CF9000F007C117D /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146EE1CF9000F007C117D /* Runner.app */, 108 | ); 109 | name = Products; 110 | sourceTree = ""; 111 | }; 112 | 97C146F01CF9000F007C117D /* Runner */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 116 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 117 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 118 | 97C147021CF9000F007C117D /* Info.plist */, 119 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 120 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 121 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 122 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 123 | ); 124 | path = Runner; 125 | sourceTree = ""; 126 | }; 127 | /* End PBXGroup section */ 128 | 129 | /* Begin PBXNativeTarget section */ 130 | 97C146ED1CF9000F007C117D /* Runner */ = { 131 | isa = PBXNativeTarget; 132 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 133 | buildPhases = ( 134 | BE8400CF301FAB326F8C763F /* [CP] Check Pods Manifest.lock */, 135 | 9740EEB61CF901F6004384FC /* Run Script */, 136 | 97C146EA1CF9000F007C117D /* Sources */, 137 | 97C146EB1CF9000F007C117D /* Frameworks */, 138 | 97C146EC1CF9000F007C117D /* Resources */, 139 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 140 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 141 | 79AB272CFF2AB432E4EA8228 /* [CP] Embed Pods Frameworks */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = Runner; 148 | productName = Runner; 149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 97C146E61CF9000F007C117D /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastUpgradeCheck = 1020; 159 | ORGANIZATIONNAME = ""; 160 | TargetAttributes = { 161 | 97C146ED1CF9000F007C117D = { 162 | CreatedOnToolsVersion = 7.3.1; 163 | LastSwiftMigration = 1100; 164 | }; 165 | }; 166 | }; 167 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 168 | compatibilityVersion = "Xcode 9.3"; 169 | developmentRegion = en; 170 | hasScannedForEncodings = 0; 171 | knownRegions = ( 172 | en, 173 | Base, 174 | ); 175 | mainGroup = 97C146E51CF9000F007C117D; 176 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 177 | projectDirPath = ""; 178 | projectRoot = ""; 179 | targets = ( 180 | 97C146ED1CF9000F007C117D /* Runner */, 181 | ); 182 | }; 183 | /* End PBXProject section */ 184 | 185 | /* Begin PBXResourcesBuildPhase section */ 186 | 97C146EC1CF9000F007C117D /* Resources */ = { 187 | isa = PBXResourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 191 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 193 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXResourcesBuildPhase section */ 198 | 199 | /* Begin PBXShellScriptBuildPhase section */ 200 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 201 | isa = PBXShellScriptBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | ); 205 | inputPaths = ( 206 | ); 207 | name = "Thin Binary"; 208 | outputPaths = ( 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | shellPath = /bin/sh; 212 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 213 | }; 214 | 79AB272CFF2AB432E4EA8228 /* [CP] Embed Pods Frameworks */ = { 215 | isa = PBXShellScriptBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | inputFileListPaths = ( 220 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 221 | ); 222 | name = "[CP] Embed Pods Frameworks"; 223 | outputFileListPaths = ( 224 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | shellPath = /bin/sh; 228 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 229 | showEnvVarsInLog = 0; 230 | }; 231 | 9740EEB61CF901F6004384FC /* Run Script */ = { 232 | isa = PBXShellScriptBuildPhase; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | ); 236 | inputPaths = ( 237 | ); 238 | name = "Run Script"; 239 | outputPaths = ( 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | shellPath = /bin/sh; 243 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 244 | }; 245 | BE8400CF301FAB326F8C763F /* [CP] Check Pods Manifest.lock */ = { 246 | isa = PBXShellScriptBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | ); 250 | inputFileListPaths = ( 251 | ); 252 | inputPaths = ( 253 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 254 | "${PODS_ROOT}/Manifest.lock", 255 | ); 256 | name = "[CP] Check Pods Manifest.lock"; 257 | outputFileListPaths = ( 258 | ); 259 | outputPaths = ( 260 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | shellPath = /bin/sh; 264 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 265 | showEnvVarsInLog = 0; 266 | }; 267 | /* End PBXShellScriptBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | 97C146EA1CF9000F007C117D /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 275 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | }; 279 | /* End PBXSourcesBuildPhase section */ 280 | 281 | /* Begin PBXVariantGroup section */ 282 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 283 | isa = PBXVariantGroup; 284 | children = ( 285 | 97C146FB1CF9000F007C117D /* Base */, 286 | ); 287 | name = Main.storyboard; 288 | sourceTree = ""; 289 | }; 290 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 97C147001CF9000F007C117D /* Base */, 294 | ); 295 | name = LaunchScreen.storyboard; 296 | sourceTree = ""; 297 | }; 298 | /* End PBXVariantGroup section */ 299 | 300 | /* Begin XCBuildConfiguration section */ 301 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_ANALYZER_NONNULL = YES; 306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 307 | CLANG_CXX_LIBRARY = "libc++"; 308 | CLANG_ENABLE_MODULES = YES; 309 | CLANG_ENABLE_OBJC_ARC = YES; 310 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 311 | CLANG_WARN_BOOL_CONVERSION = YES; 312 | CLANG_WARN_COMMA = YES; 313 | CLANG_WARN_CONSTANT_CONVERSION = YES; 314 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 315 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 316 | CLANG_WARN_EMPTY_BODY = YES; 317 | CLANG_WARN_ENUM_CONVERSION = YES; 318 | CLANG_WARN_INFINITE_RECURSION = YES; 319 | CLANG_WARN_INT_CONVERSION = YES; 320 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 325 | CLANG_WARN_STRICT_PROTOTYPES = YES; 326 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 327 | CLANG_WARN_UNREACHABLE_CODE = YES; 328 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 329 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 330 | COPY_PHASE_STRIP = NO; 331 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 332 | ENABLE_NS_ASSERTIONS = NO; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | GCC_C_LANGUAGE_STANDARD = gnu99; 335 | GCC_NO_COMMON_BLOCKS = YES; 336 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 337 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 338 | GCC_WARN_UNDECLARED_SELECTOR = YES; 339 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 340 | GCC_WARN_UNUSED_FUNCTION = YES; 341 | GCC_WARN_UNUSED_VARIABLE = YES; 342 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 343 | MTL_ENABLE_DEBUG_INFO = NO; 344 | SDKROOT = iphoneos; 345 | SUPPORTED_PLATFORMS = iphoneos; 346 | TARGETED_DEVICE_FAMILY = "1,2"; 347 | VALIDATE_PRODUCT = YES; 348 | }; 349 | name = Profile; 350 | }; 351 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 352 | isa = XCBuildConfiguration; 353 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 354 | buildSettings = { 355 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 356 | CLANG_ENABLE_MODULES = YES; 357 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 358 | DEVELOPMENT_TEAM = F5CHD3X2FV; 359 | ENABLE_BITCODE = NO; 360 | FRAMEWORK_SEARCH_PATHS = ( 361 | "$(inherited)", 362 | "$(PROJECT_DIR)/Flutter", 363 | ); 364 | INFOPLIST_FILE = Runner/Info.plist; 365 | LD_RUNPATH_SEARCH_PATHS = ( 366 | "$(inherited)", 367 | "@executable_path/Frameworks", 368 | ); 369 | LIBRARY_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | PRODUCT_BUNDLE_IDENTIFIER = com.ClubHouseClone; 374 | PRODUCT_NAME = "$(TARGET_NAME)"; 375 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 376 | SWIFT_VERSION = 5.0; 377 | VERSIONING_SYSTEM = "apple-generic"; 378 | }; 379 | name = Profile; 380 | }; 381 | 97C147031CF9000F007C117D /* Debug */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | ALWAYS_SEARCH_USER_PATHS = NO; 385 | CLANG_ANALYZER_NONNULL = YES; 386 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 387 | CLANG_CXX_LIBRARY = "libc++"; 388 | CLANG_ENABLE_MODULES = YES; 389 | CLANG_ENABLE_OBJC_ARC = YES; 390 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 391 | CLANG_WARN_BOOL_CONVERSION = YES; 392 | CLANG_WARN_COMMA = YES; 393 | CLANG_WARN_CONSTANT_CONVERSION = YES; 394 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 395 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 396 | CLANG_WARN_EMPTY_BODY = YES; 397 | CLANG_WARN_ENUM_CONVERSION = YES; 398 | CLANG_WARN_INFINITE_RECURSION = YES; 399 | CLANG_WARN_INT_CONVERSION = YES; 400 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 401 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 402 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 403 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 404 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 405 | CLANG_WARN_STRICT_PROTOTYPES = YES; 406 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 407 | CLANG_WARN_UNREACHABLE_CODE = YES; 408 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 409 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 410 | COPY_PHASE_STRIP = NO; 411 | DEBUG_INFORMATION_FORMAT = dwarf; 412 | ENABLE_STRICT_OBJC_MSGSEND = YES; 413 | ENABLE_TESTABILITY = YES; 414 | GCC_C_LANGUAGE_STANDARD = gnu99; 415 | GCC_DYNAMIC_NO_PIC = NO; 416 | GCC_NO_COMMON_BLOCKS = YES; 417 | GCC_OPTIMIZATION_LEVEL = 0; 418 | GCC_PREPROCESSOR_DEFINITIONS = ( 419 | "DEBUG=1", 420 | "$(inherited)", 421 | ); 422 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 423 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 424 | GCC_WARN_UNDECLARED_SELECTOR = YES; 425 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 426 | GCC_WARN_UNUSED_FUNCTION = YES; 427 | GCC_WARN_UNUSED_VARIABLE = YES; 428 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 429 | MTL_ENABLE_DEBUG_INFO = YES; 430 | ONLY_ACTIVE_ARCH = YES; 431 | SDKROOT = iphoneos; 432 | TARGETED_DEVICE_FAMILY = "1,2"; 433 | }; 434 | name = Debug; 435 | }; 436 | 97C147041CF9000F007C117D /* Release */ = { 437 | isa = XCBuildConfiguration; 438 | buildSettings = { 439 | ALWAYS_SEARCH_USER_PATHS = NO; 440 | CLANG_ANALYZER_NONNULL = YES; 441 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 442 | CLANG_CXX_LIBRARY = "libc++"; 443 | CLANG_ENABLE_MODULES = YES; 444 | CLANG_ENABLE_OBJC_ARC = YES; 445 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 446 | CLANG_WARN_BOOL_CONVERSION = YES; 447 | CLANG_WARN_COMMA = YES; 448 | CLANG_WARN_CONSTANT_CONVERSION = YES; 449 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 450 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 451 | CLANG_WARN_EMPTY_BODY = YES; 452 | CLANG_WARN_ENUM_CONVERSION = YES; 453 | CLANG_WARN_INFINITE_RECURSION = YES; 454 | CLANG_WARN_INT_CONVERSION = YES; 455 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 456 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 457 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 458 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 459 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 460 | CLANG_WARN_STRICT_PROTOTYPES = YES; 461 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 462 | CLANG_WARN_UNREACHABLE_CODE = YES; 463 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 464 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 465 | COPY_PHASE_STRIP = NO; 466 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 467 | ENABLE_NS_ASSERTIONS = NO; 468 | ENABLE_STRICT_OBJC_MSGSEND = YES; 469 | GCC_C_LANGUAGE_STANDARD = gnu99; 470 | GCC_NO_COMMON_BLOCKS = YES; 471 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 472 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 473 | GCC_WARN_UNDECLARED_SELECTOR = YES; 474 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 475 | GCC_WARN_UNUSED_FUNCTION = YES; 476 | GCC_WARN_UNUSED_VARIABLE = YES; 477 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 478 | MTL_ENABLE_DEBUG_INFO = NO; 479 | SDKROOT = iphoneos; 480 | SUPPORTED_PLATFORMS = iphoneos; 481 | SWIFT_COMPILATION_MODE = wholemodule; 482 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 483 | TARGETED_DEVICE_FAMILY = "1,2"; 484 | VALIDATE_PRODUCT = YES; 485 | }; 486 | name = Release; 487 | }; 488 | 97C147061CF9000F007C117D /* Debug */ = { 489 | isa = XCBuildConfiguration; 490 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 491 | buildSettings = { 492 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 493 | CLANG_ENABLE_MODULES = YES; 494 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 495 | DEVELOPMENT_TEAM = F5CHD3X2FV; 496 | ENABLE_BITCODE = NO; 497 | FRAMEWORK_SEARCH_PATHS = ( 498 | "$(inherited)", 499 | "$(PROJECT_DIR)/Flutter", 500 | ); 501 | INFOPLIST_FILE = Runner/Info.plist; 502 | LD_RUNPATH_SEARCH_PATHS = ( 503 | "$(inherited)", 504 | "@executable_path/Frameworks", 505 | ); 506 | LIBRARY_SEARCH_PATHS = ( 507 | "$(inherited)", 508 | "$(PROJECT_DIR)/Flutter", 509 | ); 510 | PRODUCT_BUNDLE_IDENTIFIER = com.ClubHouseClone; 511 | PRODUCT_NAME = "$(TARGET_NAME)"; 512 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 513 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 514 | SWIFT_VERSION = 5.0; 515 | VERSIONING_SYSTEM = "apple-generic"; 516 | }; 517 | name = Debug; 518 | }; 519 | 97C147071CF9000F007C117D /* Release */ = { 520 | isa = XCBuildConfiguration; 521 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 522 | buildSettings = { 523 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 524 | CLANG_ENABLE_MODULES = YES; 525 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 526 | DEVELOPMENT_TEAM = F5CHD3X2FV; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = ( 534 | "$(inherited)", 535 | "@executable_path/Frameworks", 536 | ); 537 | LIBRARY_SEARCH_PATHS = ( 538 | "$(inherited)", 539 | "$(PROJECT_DIR)/Flutter", 540 | ); 541 | PRODUCT_BUNDLE_IDENTIFIER = com.ClubHouseClone; 542 | PRODUCT_NAME = "$(TARGET_NAME)"; 543 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 544 | SWIFT_VERSION = 5.0; 545 | VERSIONING_SYSTEM = "apple-generic"; 546 | }; 547 | name = Release; 548 | }; 549 | /* End XCBuildConfiguration section */ 550 | 551 | /* Begin XCConfigurationList section */ 552 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 553 | isa = XCConfigurationList; 554 | buildConfigurations = ( 555 | 97C147031CF9000F007C117D /* Debug */, 556 | 97C147041CF9000F007C117D /* Release */, 557 | 249021D3217E4FDB00AE95B9 /* Profile */, 558 | ); 559 | defaultConfigurationIsVisible = 0; 560 | defaultConfigurationName = Release; 561 | }; 562 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | 97C147061CF9000F007C117D /* Debug */, 566 | 97C147071CF9000F007C117D /* Release */, 567 | 249021D4217E4FDB00AE95B9 /* Profile */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | /* End XCConfigurationList section */ 573 | }; 574 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 575 | } 576 | -------------------------------------------------------------------------------- /lib/screen/home_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:ClubHouseClone/app/locator.dart'; 2 | import 'package:ClubHouseClone/app/navigation_service.dart'; 3 | import 'package:ClubHouseClone/app/route_names.dart'; 4 | import 'package:ClubHouseClone/model/topic_model.dart'; 5 | import 'package:ClubHouseClone/share/grid_icon.dart'; 6 | import 'package:ClubHouseClone/share/icon_add.dart'; 7 | import 'package:ClubHouseClone/share/nav_bar.dart' as nav_bar; 8 | import 'package:ClubHouseClone/share/shared_style.dart'; 9 | import 'package:ClubHouseClone/share/snapping_scroll_physics.dart'; 10 | import 'package:cached_network_image/cached_network_image.dart'; 11 | import 'package:flutter/cupertino.dart'; 12 | import 'package:flutter/material.dart'; 13 | import 'package:flutter/rendering.dart'; 14 | import 'package:flutter_emoji/flutter_emoji.dart'; 15 | import 'package:visibility_detector/visibility_detector.dart'; 16 | 17 | class HomeView extends StatefulWidget { 18 | const HomeView({ 19 | Key key, 20 | }) : super(key: key); 21 | 22 | @override 23 | State createState() { 24 | return _HomeViewState(); 25 | } 26 | } 27 | 28 | class _HomeViewState extends State with WidgetsBindingObserver { 29 | NavigationService _navigationService = locator(); 30 | ScrollController _scrollController = ScrollController(initialScrollOffset: 0); 31 | bool showGridIcon = true; 32 | 33 | List topicData; 34 | GlobalKey gridIconKey = GlobalKey(); 35 | 36 | @override 37 | void initState() { 38 | super.initState(); 39 | _scrollController.addListener(_listener); 40 | topicData = TopicModelData().topicData; 41 | } 42 | 43 | _listener() { 44 | if (_scrollController.offset > 45 | _scrollController.position.maxScrollExtent + 100) { 46 | _scrollController.animateTo(0, 47 | duration: Duration(milliseconds: 100), curve: Curves.ease); 48 | } 49 | } 50 | 51 | @override 52 | void dispose() { 53 | super.dispose(); 54 | } 55 | 56 | @override 57 | void didChangeAppLifecycleState(AppLifecycleState state) { 58 | print('state = $state'); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | return CupertinoPageScaffold( 64 | backgroundColor: themeColor, 65 | navigationBar: nav_bar.CupertinoNavigationBar( 66 | border: null, 67 | backgroundColor: themeColor, 68 | leading: GestureDetector( 69 | onTap: () { 70 | _navigationService.navigateTo(SearchViewRoute); 71 | }, 72 | child: Icon( 73 | CupertinoIcons.search, 74 | color: CupertinoColors.black, 75 | ), 76 | ), 77 | trailing: Row( 78 | mainAxisAlignment: MainAxisAlignment.end, 79 | crossAxisAlignment: CrossAxisAlignment.center, 80 | children: [ 81 | Padding( 82 | padding: EdgeInsets.symmetric( 83 | horizontal: 15, 84 | ), 85 | child: GestureDetector( 86 | onTap: () => _navigationService.navigateTo(SearchViewRoute), 87 | child: Icon( 88 | CupertinoIcons.mail, 89 | color: CupertinoColors.black, 90 | ), 91 | ), 92 | ), 93 | Padding( 94 | padding: EdgeInsets.symmetric( 95 | horizontal: 15, 96 | ), 97 | child: GestureDetector( 98 | onTap: () => _navigationService.navigateTo(SearchViewRoute), 99 | child: Icon( 100 | CupertinoIcons.calendar_today, 101 | color: CupertinoColors.black, 102 | ), 103 | ), 104 | ), 105 | Padding( 106 | padding: EdgeInsets.symmetric( 107 | horizontal: 15, 108 | ), 109 | child: GestureDetector( 110 | onTap: () => _navigationService.navigateTo(SearchViewRoute), 111 | child: Icon( 112 | CupertinoIcons.bell, 113 | color: CupertinoColors.black, 114 | ), 115 | ), 116 | ), 117 | Padding( 118 | padding: EdgeInsets.symmetric( 119 | horizontal: 15, 120 | ), 121 | child: GestureDetector( 122 | onTap: () => _navigationService.navigateTo(SearchViewRoute), 123 | child: Container( 124 | height: 32, 125 | width: 33, 126 | decoration: BoxDecoration( 127 | borderRadius: BorderRadius.circular(12), 128 | ), 129 | clipBehavior: Clip.hardEdge, 130 | child: CachedNetworkImage( 131 | imageUrl: 132 | "https://images.pexels.com/photos/2078265/pexels-photo-2078265.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 133 | fit: BoxFit.cover, 134 | ), 135 | ), 136 | ), 137 | ), 138 | ], 139 | ), 140 | ), 141 | child: ListView( 142 | shrinkWrap: false, 143 | scrollDirection: Axis.horizontal, 144 | controller: _scrollController, 145 | physics: SnappingScrollPhysics( 146 | itemExtent: MediaQuery.of(context).size.width / 1.3, 147 | ), 148 | children: [ 149 | _mainView(), 150 | _slideView(), 151 | ], 152 | ), 153 | ); 154 | } 155 | 156 | Widget _buildTitle() { 157 | return GestureDetector( 158 | onTap: () => _navigationService.navigateTo(SearchViewRoute), 159 | child: Container( 160 | color: themeColor, 161 | child: Column( 162 | children: [ 163 | SizedBox( 164 | height: 25, 165 | ), 166 | Container( 167 | alignment: Alignment.center, 168 | child: Text( 169 | "Clubhouse is full of interesting people! \n Try following at least 25.", 170 | textAlign: TextAlign.center, 171 | textScaleFactor: 1, 172 | style: TextStyle( 173 | color: black, 174 | fontWeight: FontWeight.bold, 175 | // fontSize: 12, 176 | ), 177 | ), 178 | ), 179 | SizedBox( 180 | height: 10, 181 | ), 182 | Container( 183 | height: 115, 184 | width: double.infinity, 185 | alignment: Alignment.center, 186 | child: Container( 187 | width: 260, 188 | child: Stack( 189 | children: [ 190 | Positioned.fill( 191 | child: Align( 192 | alignment: Alignment.center, 193 | child: Stack( 194 | children: [ 195 | Positioned( 196 | left: 30, 197 | top: 0, 198 | child: IconAdd( 199 | size: 38, 200 | imageUrl: 201 | "https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&h=750&w=1260", 202 | ), 203 | ), 204 | Positioned( 205 | left: 5, 206 | top: 35, 207 | child: IconAdd( 208 | size: 25, 209 | imageUrl: 210 | "https://images.pexels.com/photos/2078265/pexels-photo-2078265.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 211 | ), 212 | ), 213 | Positioned( 214 | left: 80, 215 | top: 0, 216 | child: IconAdd( 217 | size: 49, 218 | imageUrl: 219 | "https://images.pexels.com/photos/1310522/pexels-photo-1310522.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260"), 220 | ), 221 | Positioned( 222 | left: 50, 223 | top: 45, 224 | child: IconAdd( 225 | size: 38, 226 | imageUrl: 227 | "https://images.pexels.com/photos/1043471/pexels-photo-1043471.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 228 | ), 229 | ), 230 | Positioned( 231 | left: 120, 232 | top: 25, 233 | child: IconAdd( 234 | size: 60, 235 | imageUrl: 236 | "https://images.pexels.com/photos/1382731/pexels-photo-1382731.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 237 | ), 238 | ), 239 | Positioned( 240 | left: 175, 241 | top: 0, 242 | child: IconAdd( 243 | size: 35, 244 | imageUrl: 245 | "https://images.pexels.com/photos/697509/pexels-photo-697509.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 246 | ), 247 | ), 248 | Positioned( 249 | left: 200, 250 | top: 35, 251 | child: IconAdd( 252 | size: 35, 253 | imageUrl: 254 | "https://images.pexels.com/photos/943084/pexels-photo-943084.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 255 | ), 256 | ), 257 | Positioned( 258 | left: 220, 259 | top: 10, 260 | child: IconAdd( 261 | size: 22, 262 | imageUrl: 263 | "https://images.pexels.com/photos/1222271/pexels-photo-1222271.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", 264 | ), 265 | ), 266 | ], 267 | ), 268 | ), 269 | ) 270 | ], 271 | ), 272 | ), 273 | ), 274 | Container( 275 | margin: EdgeInsets.symmetric(horizontal: 80), 276 | child: CupertinoButton( 277 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 0), 278 | borderRadius: BorderRadius.circular(20), 279 | minSize: 30, 280 | color: brown.withOpacity(0.1), 281 | child: Row( 282 | mainAxisAlignment: MainAxisAlignment.center, 283 | children: [ 284 | Icon( 285 | CupertinoIcons.person_crop_circle_badge_plus, 286 | color: brown, 287 | size: 15, 288 | ), 289 | SizedBox( 290 | width: 5, 291 | ), 292 | Text( 293 | "Find more people to follow", 294 | textScaleFactor: 1, 295 | style: TextStyle( 296 | color: brown, 297 | fontWeight: FontWeight.bold, 298 | ), 299 | ), 300 | ], 301 | ), 302 | onPressed: () => _navigationService.navigateTo(SearchViewRoute), 303 | ), 304 | ), 305 | SizedBox( 306 | height: 45, 307 | ), 308 | ], 309 | ), 310 | ), 311 | ); 312 | } 313 | 314 | Widget _slideView() { 315 | return Container( 316 | color: themeColor, 317 | alignment: Alignment.center, 318 | width: MediaQuery.of(context).size.width / 1.3, 319 | child: Column( 320 | mainAxisSize: MainAxisSize.min, 321 | mainAxisAlignment: MainAxisAlignment.center, 322 | children: [ 323 | Text( 324 | "No one you follow is online", 325 | textScaleFactor: 1, 326 | style: TextStyle( 327 | fontWeight: FontWeight.bold, 328 | ), 329 | ), 330 | Container( 331 | child: CupertinoButton( 332 | color: brown.withOpacity(0.1), 333 | borderRadius: BorderRadius.circular(20), 334 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 0), 335 | minSize: 30, 336 | child: Text( 337 | "Find people to follow", 338 | textScaleFactor: 1, 339 | style: TextStyle( 340 | color: brown, 341 | fontWeight: FontWeight.bold, 342 | ), 343 | ), 344 | onPressed: () => _navigationService.navigateTo(SearchViewRoute), 345 | ), 346 | ), 347 | ], 348 | ), 349 | ); 350 | } 351 | 352 | Widget _mainView() { 353 | return VisibilityDetector( 354 | key: Key("main_page"), 355 | onVisibilityChanged: (VisibilityInfo info) { 356 | if (gridIconKey.currentState != null) { 357 | if (info.visibleFraction == 1) { 358 | gridIconKey.currentState.changeState(true); 359 | } else { 360 | gridIconKey.currentState.changeState(false); 361 | } 362 | } 363 | }, 364 | child: Container( 365 | width: MediaQuery.of(context).size.width, 366 | child: Stack( 367 | children: [ 368 | CustomScrollView( 369 | slivers: [ 370 | SliverToBoxAdapter( 371 | child: _buildTitle(), 372 | ), 373 | SliverList( 374 | delegate: SliverChildBuilderDelegate( 375 | (BuildContext context, int index) { 376 | return Container( 377 | width: double.infinity, 378 | decoration: BoxDecoration( 379 | color: white, 380 | borderRadius: BorderRadius.circular(20), 381 | boxShadow: [ 382 | BoxShadow( 383 | color: brown.withOpacity(0.2), 384 | spreadRadius: 0.1, 385 | blurRadius: 0.2, 386 | offset: Offset(0, 1), 387 | ) 388 | ], 389 | ), 390 | margin: 391 | EdgeInsets.only(left: 16, right: 16, bottom: 16), 392 | padding: EdgeInsets.all(20), 393 | child: Column( 394 | mainAxisSize: MainAxisSize.min, 395 | mainAxisAlignment: MainAxisAlignment.start, 396 | crossAxisAlignment: CrossAxisAlignment.start, 397 | children: [ 398 | topicData[index].clubName != null 399 | ? Text( 400 | topicData[index].clubName.toUpperCase() + 401 | " ${EmojiParser().get("house").code}", 402 | textScaleFactor: 1, 403 | style: TextStyle( 404 | fontWeight: FontWeight.w600, 405 | ), 406 | ) 407 | : Container(), 408 | Text( 409 | topicData[index].topicTitle, 410 | textScaleFactor: 1, 411 | maxLines: 2, 412 | overflow: TextOverflow.ellipsis, 413 | style: TextStyle( 414 | fontSize: 16, 415 | fontWeight: FontWeight.bold, 416 | ), 417 | ), 418 | Container( 419 | child: Row( 420 | children: [ 421 | Container( 422 | height: 70, 423 | width: 100, 424 | child: Stack( 425 | children: [ 426 | Container( 427 | height: 45, 428 | width: 45, 429 | decoration: BoxDecoration( 430 | borderRadius: 431 | BorderRadius.circular(20), 432 | ), 433 | clipBehavior: Clip.hardEdge, 434 | child: CachedNetworkImage( 435 | imageUrl: topicData[index] 436 | .users[1] 437 | .userImage, 438 | fit: BoxFit.cover, 439 | ), 440 | ), 441 | Positioned( 442 | left: 30, 443 | top: 25, 444 | child: Container( 445 | height: 45, 446 | width: 45, 447 | decoration: BoxDecoration( 448 | borderRadius: 449 | BorderRadius.circular(20), 450 | ), 451 | clipBehavior: Clip.hardEdge, 452 | child: CachedNetworkImage( 453 | imageUrl: topicData[index] 454 | .users[0] 455 | .userImage, 456 | fit: BoxFit.cover, 457 | ), 458 | ), 459 | ), 460 | ], 461 | ), 462 | ), 463 | Expanded( 464 | child: Container( 465 | child: Column( 466 | crossAxisAlignment: 467 | CrossAxisAlignment.start, 468 | children: [ 469 | ...List.generate( 470 | topicData[index].users.length, (i) { 471 | return Text( 472 | topicData[index].users[i].userName + 473 | " ${EmojiParser().get("speech_balloon").code}", 474 | textScaleFactor: 1, 475 | style: TextStyle( 476 | fontWeight: FontWeight.w600, 477 | fontSize: 18), 478 | ); 479 | }), 480 | SizedBox( 481 | height: 5, 482 | ), 483 | Row( 484 | children: [ 485 | Text( 486 | "${topicData[index].audienceNumber} ", 487 | textScaleFactor: 1, 488 | style: TextStyle(fontSize: 10), 489 | ), 490 | Icon( 491 | Icons.person, 492 | color: brown, 493 | size: 12, 494 | ), 495 | Text( 496 | " / ", 497 | textScaleFactor: 1, 498 | style: TextStyle(fontSize: 11), 499 | ), 500 | Text( 501 | "${topicData[index].speechNumber} ", 502 | textScaleFactor: 1, 503 | style: TextStyle(fontSize: 10), 504 | ), 505 | Icon( 506 | CupertinoIcons.chat_bubble_text, 507 | color: brown, 508 | size: 12, 509 | ), 510 | ], 511 | ), 512 | ], 513 | ), 514 | )), 515 | ], 516 | ), 517 | ), 518 | ], 519 | ), 520 | ); 521 | }, 522 | childCount: topicData.length, 523 | ), 524 | ), 525 | SliverToBoxAdapter( 526 | child: _explore(), 527 | ), 528 | ], 529 | ), 530 | Positioned( 531 | bottom: 0, 532 | left: 0, 533 | right: 0, 534 | height: 100, 535 | child: _bottomView(), 536 | ), 537 | ], 538 | ), 539 | ), 540 | ); 541 | } 542 | 543 | Widget _explore() { 544 | return Column( 545 | children: [ 546 | Container( 547 | width: 150, 548 | padding: EdgeInsets.symmetric(horizontal: 15), 549 | child: CupertinoButton( 550 | borderRadius: BorderRadius.circular(20), 551 | padding: EdgeInsets.symmetric(horizontal: 20), 552 | color: brown.withOpacity(0.1), 553 | child: Row( 554 | mainAxisAlignment: MainAxisAlignment.center, 555 | children: [ 556 | Text( 557 | EmojiParser().get("earth_asia").code, 558 | textScaleFactor: 1, 559 | ), 560 | SizedBox( 561 | width: 5, 562 | ), 563 | Text( 564 | "Explore", 565 | textScaleFactor: 1, 566 | style: TextStyle( 567 | color: brown, 568 | fontWeight: FontWeight.bold, 569 | ), 570 | ), 571 | ], 572 | ), 573 | onPressed: () => _navigationService.navigateTo(SearchViewRoute), 574 | ), 575 | ), 576 | SizedBox(height: 150), 577 | ], 578 | ); 579 | } 580 | 581 | Widget _bottomView() { 582 | return Container( 583 | alignment: Alignment.topCenter, 584 | child: Column( 585 | children: [ 586 | Container( 587 | decoration: BoxDecoration( 588 | gradient: LinearGradient( 589 | colors: [themeColor.withOpacity(0.1), themeColor], 590 | begin: Alignment.topCenter, 591 | end: Alignment.bottomCenter), 592 | ), 593 | child: Row( 594 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 595 | children: [ 596 | Expanded( 597 | flex: 1, 598 | child: SizedBox(), 599 | ), 600 | Expanded( 601 | flex: 2, 602 | child: Container( 603 | padding: EdgeInsets.symmetric(horizontal: 20), 604 | child: CupertinoButton( 605 | borderRadius: BorderRadius.circular(20), 606 | padding: EdgeInsets.symmetric(horizontal: 20), 607 | color: green, 608 | child: Row( 609 | mainAxisAlignment: MainAxisAlignment.center, 610 | children: [ 611 | Icon( 612 | CupertinoIcons.plus, 613 | color: white, 614 | size: 18, 615 | ), 616 | SizedBox( 617 | width: 5, 618 | ), 619 | Text( 620 | "Start a room", 621 | textScaleFactor: 1, 622 | style: TextStyle( 623 | color: white, 624 | fontWeight: FontWeight.bold, 625 | ), 626 | ), 627 | ], 628 | ), 629 | onPressed: () => 630 | _navigationService.navigateTo(SearchViewRoute), 631 | ), 632 | ), 633 | ), 634 | Expanded( 635 | flex: 1, 636 | child: GridIcon( 637 | key: gridIconKey, 638 | onTap: () { 639 | _scrollController.animateTo( 640 | _scrollController.position.maxScrollExtent, 641 | duration: Duration(milliseconds: 400), 642 | curve: Curves.ease, 643 | ); 644 | }, 645 | ), 646 | ), 647 | ], 648 | ), 649 | ), 650 | Expanded( 651 | child: Container( 652 | color: themeColor, 653 | ), 654 | ), 655 | ], 656 | ), 657 | ); 658 | } 659 | } 660 | -------------------------------------------------------------------------------- /lib/share/route.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // @dart = 2.8 6 | 7 | import 'dart:async'; 8 | import 'dart:math'; 9 | import 'dart:ui' show lerpDouble, ImageFilter; 10 | 11 | import 'package:flutter/cupertino.dart'; 12 | import 'package:flutter/foundation.dart'; 13 | import 'package:flutter/gestures.dart'; 14 | import 'package:flutter/rendering.dart'; 15 | import 'package:flutter/widgets.dart'; 16 | import 'package:flutter/animation.dart' show Curves; 17 | 18 | const double _kBackGestureWidth = 20.0; 19 | const double _kMinFlingVelocity = 1.0; // Screen widths per second. 20 | 21 | // An eyeballed value for the maximum time it takes for a page to animate forward 22 | // if the user releases a page mid swipe. 23 | const int _kMaxDroppedSwipePageForwardAnimationTime = 800; // Milliseconds. 24 | 25 | // The maximum time for a page to get reset to it's original position if the 26 | // user releases a page mid swipe. 27 | const int _kMaxPageBackAnimationTime = 300; // Milliseconds. 28 | 29 | // Barrier color for a Cupertino modal barrier. 30 | // Extracted from https://developer.apple.com/design/resources/. 31 | const Color _kModalBarrierColor = CupertinoDynamicColor.withBrightness( 32 | color: Color(0x33000000), 33 | darkColor: Color(0x7A000000), 34 | ); 35 | 36 | // The duration of the transition used when a modal popup is shown. 37 | const Duration _kModalPopupTransitionDuration = Duration(milliseconds: 335); 38 | 39 | // Offset from offscreen to the right to fully on screen. 40 | final Animatable _kRightMiddleTween = Tween( 41 | begin: const Offset(1.0, 0.0), 42 | end: Offset.zero, 43 | ); 44 | 45 | // Offset from fully on screen to 1/3 offscreen to the left. 46 | final Animatable _kMiddleLeftTween = Tween( 47 | begin: Offset.zero, 48 | end: const Offset(-1.0 / 3.0, 0.0), 49 | ); 50 | 51 | // Offset from offscreen below to fully on screen. 52 | final Animatable _kBottomUpTween = Tween( 53 | begin: const Offset(0.0, 1.0), 54 | end: Offset.zero, 55 | ); 56 | 57 | // Custom decoration from no shadow to page shadow mimicking iOS page 58 | // transitions using gradients. 59 | final DecorationTween _kGradientShadowTween = DecorationTween( 60 | begin: _CupertinoEdgeShadowDecoration.none, // No decoration initially. 61 | end: const _CupertinoEdgeShadowDecoration( 62 | edgeGradient: LinearGradient( 63 | // Spans 5% of the page. 64 | begin: AlignmentDirectional.centerStart, 65 | end: AlignmentDirectional.centerEnd, 66 | // Eyeballed gradient used to mimic a drop shadow on the start side only. 67 | colors: [ 68 | Color(0x00000000), 69 | Color(0x04000000), 70 | Color(0x12000000), 71 | Color(0x38000000), 72 | ], 73 | stops: [0.0, 0.3, 0.6, 1.0], 74 | ), 75 | ), 76 | ); 77 | 78 | /// A mixin that replaces the entire screen with an iOS transition for a 79 | /// [PageRoute]. 80 | /// 81 | /// {@template flutter.cupertino.cupertinoRouteTransitionMixin} 82 | /// The page slides in from the right and exits in reverse. The page also shifts 83 | /// to the left in parallax when another page enters to cover it. 84 | /// 85 | /// The page slides in from the bottom and exits in reverse with no parallax 86 | /// effect for fullscreen dialogs. 87 | /// {@endtemplate} 88 | /// 89 | /// See also: 90 | /// 91 | /// * [MaterialRouteTransitionMixin], which is a mixin that provides 92 | /// platform-appropriate transitions for a [PageRoute] 93 | /// * [CupertinoPageRoute], which is a [PageRoute] that leverages this mixin. 94 | mixin CupertinoRouteTransitionMixin on PageRoute { 95 | /// Builds the primary contents of the route. 96 | @protected 97 | Widget buildContent(BuildContext context); 98 | 99 | /// {@template flutter.cupertino.cupertinoRouteTransitionMixin.title} 100 | /// A title string for this route. 101 | /// 102 | /// Used to auto-populate [CupertinoNavigationBar] and 103 | /// [CupertinoSliverNavigationBar]'s `middle`/`largeTitle` widgets when 104 | /// one is not manually supplied. 105 | /// {@endtemplate} 106 | String get title; 107 | 108 | ValueNotifier _previousTitle; 109 | 110 | /// The title string of the previous [CupertinoPageRoute]. 111 | /// 112 | /// The [ValueListenable]'s value is readable after the route is installed 113 | /// onto a [Navigator]. The [ValueListenable] will also notify its listeners 114 | /// if the value changes (such as by replacing the previous route). 115 | /// 116 | /// The [ValueListenable] itself will be null before the route is installed. 117 | /// Its content value will be null if the previous route has no title or 118 | /// is not a [CupertinoPageRoute]. 119 | /// 120 | /// See also: 121 | /// 122 | /// * [ValueListenableBuilder], which can be used to listen and rebuild 123 | /// widgets based on a ValueListenable. 124 | ValueListenable get previousTitle { 125 | assert( 126 | _previousTitle != null, 127 | 'Cannot read the previousTitle for a route that has not yet been installed', 128 | ); 129 | return _previousTitle; 130 | } 131 | 132 | @override 133 | void didChangePrevious(Route previousRoute) { 134 | final String previousTitleString = 135 | previousRoute is CupertinoRouteTransitionMixin 136 | ? previousRoute.title 137 | : null; 138 | if (_previousTitle == null) { 139 | _previousTitle = ValueNotifier(previousTitleString); 140 | } else { 141 | _previousTitle.value = previousTitleString; 142 | } 143 | super.didChangePrevious(previousRoute); 144 | } 145 | 146 | @override 147 | // A relatively rigorous eyeball estimation. 148 | Duration get transitionDuration => const Duration(milliseconds: 400); 149 | 150 | @override 151 | Color get barrierColor => null; 152 | 153 | @override 154 | String get barrierLabel => null; 155 | 156 | @override 157 | bool canTransitionTo(TransitionRoute nextRoute) { 158 | // Don't perform outgoing animation if the next route is a fullscreen dialog. 159 | return nextRoute is CupertinoRouteTransitionMixin && 160 | !nextRoute.fullscreenDialog; 161 | } 162 | 163 | /// True if an iOS-style back swipe pop gesture is currently underway for [route]. 164 | /// 165 | /// This just check the route's [NavigatorState.userGestureInProgress]. 166 | /// 167 | /// See also: 168 | /// 169 | /// * [popGestureEnabled], which returns true if a user-triggered pop gesture 170 | /// would be allowed. 171 | static bool isPopGestureInProgress(PageRoute route) { 172 | return route.navigator.userGestureInProgress; 173 | } 174 | 175 | /// True if an iOS-style back swipe pop gesture is currently underway for this route. 176 | /// 177 | /// See also: 178 | /// 179 | /// * [isPopGestureInProgress], which returns true if a Cupertino pop gesture 180 | /// is currently underway for specific route. 181 | /// * [popGestureEnabled], which returns true if a user-triggered pop gesture 182 | /// would be allowed. 183 | bool get popGestureInProgress => isPopGestureInProgress(this); 184 | 185 | /// Whether a pop gesture can be started by the user. 186 | /// 187 | /// Returns true if the user can edge-swipe to a previous route. 188 | /// 189 | /// Returns false once [isPopGestureInProgress] is true, but 190 | /// [isPopGestureInProgress] can only become true if [popGestureEnabled] was 191 | /// true first. 192 | /// 193 | /// This should only be used between frames, not during build. 194 | bool get popGestureEnabled => _isPopGestureEnabled(this); 195 | 196 | static bool _isPopGestureEnabled(PageRoute route) { 197 | // If there's nothing to go back to, then obviously we don't support 198 | // the back gesture. 199 | if (route.isFirst) return false; 200 | // If the route wouldn't actually pop if we popped it, then the gesture 201 | // would be really confusing (or would skip internal routes), so disallow it. 202 | if (route.willHandlePopInternally) return false; 203 | // If attempts to dismiss this route might be vetoed such as in a page 204 | // with forms, then do not allow the user to dismiss the route with a swipe. 205 | if (route.hasScopedWillPopCallback) return false; 206 | // Fullscreen dialogs aren't dismissible by back swipe. 207 | if (route.fullscreenDialog) return false; 208 | // If we're in an animation already, we cannot be manually swiped. 209 | if (route.animation.status != AnimationStatus.completed) return false; 210 | // If we're being popped into, we also cannot be swiped until the pop above 211 | // it completes. This translates to our secondary animation being 212 | // dismissed. 213 | if (route.secondaryAnimation.status != AnimationStatus.dismissed) 214 | return false; 215 | // If we're in a gesture already, we cannot start another. 216 | if (isPopGestureInProgress(route)) return false; 217 | 218 | // Looks like a back gesture would be welcome! 219 | return true; 220 | } 221 | 222 | @override 223 | Widget buildPage(BuildContext context, Animation animation, 224 | Animation secondaryAnimation) { 225 | final Widget child = buildContent(context); 226 | final Widget result = Semantics( 227 | scopesRoute: true, 228 | explicitChildNodes: true, 229 | child: child, 230 | ); 231 | assert(() { 232 | if (child == null) { 233 | throw FlutterError.fromParts([ 234 | ErrorSummary( 235 | 'The builder for route "${settings.name}" returned null.'), 236 | ErrorDescription('Route builders must never return null.'), 237 | ]); 238 | } 239 | return true; 240 | }()); 241 | return result; 242 | } 243 | 244 | // Called by _CupertinoBackGestureDetector when a pop ("back") drag start 245 | // gesture is detected. The returned controller handles all of the subsequent 246 | // drag events. 247 | static _CupertinoBackGestureController _startPopGesture( 248 | PageRoute route) { 249 | assert(_isPopGestureEnabled(route)); 250 | 251 | return _CupertinoBackGestureController( 252 | navigator: route.navigator, 253 | controller: route.controller, // protected access 254 | ); 255 | } 256 | 257 | /// Returns a [CupertinoFullscreenDialogTransition] if [route] is a full 258 | /// screen dialog, otherwise a [CupertinoPageTransition] is returned. 259 | /// 260 | /// Used by [CupertinoPageRoute.buildTransitions]. 261 | /// 262 | /// This method can be applied to any [PageRoute], not just 263 | /// [CupertinoPageRoute]. It's typically used to provide a Cupertino style 264 | /// horizontal transition for material widgets when the target platform 265 | /// is [TargetPlatform.iOS]. 266 | /// 267 | /// See also: 268 | /// 269 | /// * [CupertinoPageTransitionsBuilder], which uses this method to define a 270 | /// [PageTransitionsBuilder] for the [PageTransitionsTheme]. 271 | static Widget buildPageTransitions( 272 | PageRoute route, 273 | BuildContext context, 274 | Animation animation, 275 | Animation secondaryAnimation, 276 | Widget child, 277 | ) { 278 | // Check if the route has an animation that's currently participating 279 | // in a back swipe gesture. 280 | // 281 | // In the middle of a back gesture drag, let the transition be linear to 282 | // match finger motions. 283 | final bool linearTransition = isPopGestureInProgress(route); 284 | if (route.fullscreenDialog) { 285 | return CupertinoFullscreenDialogTransition( 286 | primaryRouteAnimation: animation, 287 | secondaryRouteAnimation: secondaryAnimation, 288 | child: child, 289 | linearTransition: linearTransition, 290 | ); 291 | } else { 292 | return CupertinoPageTransition( 293 | primaryRouteAnimation: animation, 294 | secondaryRouteAnimation: secondaryAnimation, 295 | linearTransition: linearTransition, 296 | child: _CupertinoBackGestureDetector( 297 | enabledCallback: () => _isPopGestureEnabled(route), 298 | onStartPopGesture: () => _startPopGesture(route), 299 | child: child, 300 | ), 301 | ); 302 | } 303 | } 304 | 305 | @override 306 | Widget buildTransitions(BuildContext context, Animation animation, 307 | Animation secondaryAnimation, Widget child) { 308 | return buildPageTransitions( 309 | this, context, animation, secondaryAnimation, child); 310 | } 311 | } 312 | 313 | /// A modal route that replaces the entire screen with an iOS transition. 314 | /// 315 | /// {@macro flutter.cupertino.cupertinoRouteTransitionMixin} 316 | /// 317 | /// By default, when a modal route is replaced by another, the previous route 318 | /// remains in memory. To free all the resources when this is not necessary, set 319 | /// [maintainState] to false. 320 | /// 321 | /// The type `T` specifies the return type of the route which can be supplied as 322 | /// the route is popped from the stack via [Navigator.pop] when an optional 323 | /// `result` can be provided. 324 | /// 325 | /// See also: 326 | /// 327 | /// * [CupertinoRouteTransitionMixin], for a mixin that provides iOS transition 328 | /// for this modal route. 329 | /// * [MaterialPageRoute], for an adaptive [PageRoute] that uses a 330 | /// platform-appropriate transition. 331 | /// * [CupertinoPageScaffold], for applications that have one page with a fixed 332 | /// navigation bar on top. 333 | /// * [CupertinoTabScaffold], for applications that have a tab bar at the 334 | /// bottom with multiple pages. 335 | /// * [CupertinoPage], for a [Page] version of this class. 336 | class CupertinoPageRoute extends PageRoute 337 | with CupertinoRouteTransitionMixin { 338 | /// Creates a page route for use in an iOS designed app. 339 | /// 340 | /// The [builder], [maintainState], and [fullscreenDialog] arguments must not 341 | /// be null. 342 | CupertinoPageRoute({ 343 | @required this.builder, 344 | this.title, 345 | RouteSettings settings, 346 | this.maintainState = true, 347 | bool fullscreenDialog = false, 348 | }) : assert(builder != null), 349 | assert(maintainState != null), 350 | assert(fullscreenDialog != null), 351 | assert(opaque), 352 | super(settings: settings, fullscreenDialog: fullscreenDialog); 353 | 354 | /// Builds the primary contents of the route. 355 | final WidgetBuilder builder; 356 | 357 | @override 358 | final String title; 359 | 360 | @override 361 | Widget buildContent(BuildContext context) => builder(context); 362 | 363 | @override 364 | final bool maintainState; 365 | 366 | @override 367 | String get debugLabel => '${super.debugLabel}(${settings.name})'; 368 | } 369 | 370 | // A page-based version of CupertinoPageRoute. 371 | // 372 | // This route uses the builder from the page to build its content. This ensures 373 | // the content is up to date after page updates. 374 | class _PageBasedCupertinoPageRoute extends PageRoute 375 | with CupertinoRouteTransitionMixin { 376 | _PageBasedCupertinoPageRoute({ 377 | @required CupertinoPage page, 378 | }) : assert(page != null), 379 | assert(opaque), 380 | super(settings: page); 381 | 382 | CupertinoPage get _page => settings as CupertinoPage; 383 | 384 | @override 385 | Widget buildContent(BuildContext context) => _page.child; 386 | 387 | @override 388 | String get title => _page.title; 389 | 390 | @override 391 | bool get maintainState => _page.maintainState; 392 | 393 | @override 394 | bool get fullscreenDialog => _page.fullscreenDialog; 395 | 396 | @override 397 | String get debugLabel => '${super.debugLabel}(${_page.name})'; 398 | } 399 | 400 | /// A page that creates a cupertino style [PageRoute]. 401 | /// 402 | /// {@macro flutter.cupertino.cupertinoRouteTransitionMixin} 403 | /// 404 | /// By default, when a created modal route is replaced by another, the previous 405 | /// route remains in memory. To free all the resources when this is not 406 | /// necessary, set [maintainState] to false. 407 | /// 408 | /// The type `T` specifies the return type of the route which can be supplied as 409 | /// the route is popped from the stack via [Navigator.transitionDelegate] by 410 | /// providing the optional `result` argument to the 411 | /// [RouteTransitionRecord.markForPop] in the [TransitionDelegate.resolve]. 412 | /// 413 | /// See also: 414 | /// 415 | /// * [CupertinoPageRoute], for a [PageRoute] version of this class. 416 | class CupertinoPage extends Page { 417 | /// Creates a cupertino page. 418 | const CupertinoPage({ 419 | @required this.child, 420 | this.maintainState = true, 421 | this.title, 422 | this.fullscreenDialog = false, 423 | LocalKey key, 424 | String name, 425 | Object arguments, 426 | }) : assert(child != null), 427 | assert(maintainState != null), 428 | assert(fullscreenDialog != null), 429 | super(key: key, name: name, arguments: arguments); 430 | 431 | /// The content to be shown in the [Route] created by this page. 432 | final Widget child; 433 | 434 | /// {@macro flutter.cupertino.cupertinoRouteTransitionMixin.title} 435 | final String title; 436 | 437 | /// {@macro flutter.widgets.modalRoute.maintainState} 438 | final bool maintainState; 439 | 440 | /// {@macro flutter.widgets.pageRoute.fullscreenDialog} 441 | final bool fullscreenDialog; 442 | 443 | @override 444 | Route createRoute(BuildContext context) { 445 | return _PageBasedCupertinoPageRoute(page: this); 446 | } 447 | } 448 | 449 | /// Provides an iOS-style page transition animation. 450 | /// 451 | /// The page slides in from the right and exits in reverse. It also shifts to the left in 452 | /// a parallax motion when another page enters to cover it. 453 | class CupertinoPageTransition extends StatelessWidget { 454 | /// Creates an iOS-style page transition. 455 | /// 456 | /// * `primaryRouteAnimation` is a linear route animation from 0.0 to 1.0 457 | /// when this screen is being pushed. 458 | /// * `secondaryRouteAnimation` is a linear route animation from 0.0 to 1.0 459 | /// when another screen is being pushed on top of this one. 460 | /// * `linearTransition` is whether to perform the transitions linearly. 461 | /// Used to precisely track back gesture drags. 462 | CupertinoPageTransition({ 463 | Key key, 464 | @required Animation primaryRouteAnimation, 465 | @required Animation secondaryRouteAnimation, 466 | @required this.child, 467 | @required bool linearTransition, 468 | }) : assert(linearTransition != null), 469 | _primaryPositionAnimation = (linearTransition 470 | ? primaryRouteAnimation 471 | : CurvedAnimation( 472 | // The curves below have been rigorously derived from plots of native 473 | // iOS animation frames. Specifically, a video was taken of a page 474 | // transition animation and the distance in each frame that the page 475 | // moved was measured. A best fit bezier curve was the fitted to the 476 | // point set, which is linearToEaseIn. Conversely, easeInToLinear is the 477 | // reflection over the origin of linearToEaseIn. 478 | parent: primaryRouteAnimation, 479 | curve: Curves.linearToEaseOut, 480 | reverseCurve: Curves.easeInToLinear, 481 | )) 482 | .drive(_kRightMiddleTween), 483 | _secondaryPositionAnimation = (linearTransition 484 | ? secondaryRouteAnimation 485 | : CurvedAnimation( 486 | parent: secondaryRouteAnimation, 487 | curve: Curves.linearToEaseOut, 488 | reverseCurve: Curves.easeInToLinear, 489 | )) 490 | .drive(_kMiddleLeftTween), 491 | _primaryShadowAnimation = (linearTransition 492 | ? primaryRouteAnimation 493 | : CurvedAnimation( 494 | parent: primaryRouteAnimation, 495 | curve: Curves.linearToEaseOut, 496 | )) 497 | .drive(_kGradientShadowTween), 498 | super(key: key); 499 | 500 | // When this page is coming in to cover another page. 501 | final Animation _primaryPositionAnimation; 502 | // When this page is becoming covered by another page. 503 | final Animation _secondaryPositionAnimation; 504 | final Animation _primaryShadowAnimation; 505 | 506 | /// The widget below this widget in the tree. 507 | final Widget child; 508 | 509 | @override 510 | Widget build(BuildContext context) { 511 | assert(debugCheckHasDirectionality(context)); 512 | final TextDirection textDirection = Directionality.of(context); 513 | return SlideTransition( 514 | position: _secondaryPositionAnimation, 515 | textDirection: textDirection, 516 | transformHitTests: false, 517 | child: SlideTransition( 518 | position: _primaryPositionAnimation, 519 | textDirection: textDirection, 520 | child: DecoratedBoxTransition( 521 | decoration: _primaryShadowAnimation, 522 | child: child, 523 | ), 524 | ), 525 | ); 526 | } 527 | } 528 | 529 | /// An iOS-style transition used for summoning fullscreen dialogs. 530 | /// 531 | /// For example, used when creating a new calendar event by bringing in the next 532 | /// screen from the bottom. 533 | class CupertinoFullscreenDialogTransition extends StatelessWidget { 534 | /// Creates an iOS-style transition used for summoning fullscreen dialogs. 535 | /// 536 | /// * `primaryRouteAnimation` is a linear route animation from 0.0 to 1.0 537 | /// when this screen is being pushed. 538 | /// * `secondaryRouteAnimation` is a linear route animation from 0.0 to 1.0 539 | /// when another screen is being pushed on top of this one. 540 | /// * `linearTransition` is whether to perform the secondary transition linearly. 541 | /// Used to precisely track back gesture drags. 542 | CupertinoFullscreenDialogTransition({ 543 | Key key, 544 | @required Animation primaryRouteAnimation, 545 | @required Animation secondaryRouteAnimation, 546 | @required this.child, 547 | @required bool linearTransition, 548 | }) : _positionAnimation = CurvedAnimation( 549 | parent: primaryRouteAnimation, 550 | curve: Curves.linearToEaseOut, 551 | // The curve must be flipped so that the reverse animation doesn't play 552 | // an ease-in curve, which iOS does not use. 553 | reverseCurve: Curves.linearToEaseOut.flipped, 554 | ).drive(_kBottomUpTween), 555 | _secondaryPositionAnimation = (linearTransition 556 | ? secondaryRouteAnimation 557 | : CurvedAnimation( 558 | parent: secondaryRouteAnimation, 559 | curve: Curves.linearToEaseOut, 560 | reverseCurve: Curves.easeInToLinear, 561 | )) 562 | .drive(_kMiddleLeftTween), 563 | super(key: key); 564 | 565 | final Animation _positionAnimation; 566 | // When this page is becoming covered by another page. 567 | final Animation _secondaryPositionAnimation; 568 | 569 | /// The widget below this widget in the tree. 570 | final Widget child; 571 | 572 | @override 573 | Widget build(BuildContext context) { 574 | assert(debugCheckHasDirectionality(context)); 575 | final TextDirection textDirection = Directionality.of(context); 576 | return SlideTransition( 577 | position: _secondaryPositionAnimation, 578 | textDirection: textDirection, 579 | transformHitTests: false, 580 | child: SlideTransition( 581 | position: _positionAnimation, 582 | child: child, 583 | ), 584 | ); 585 | } 586 | } 587 | 588 | /// This is the widget side of [_CupertinoBackGestureController]. 589 | /// 590 | /// This widget provides a gesture recognizer which, when it determines the 591 | /// route can be closed with a back gesture, creates the controller and 592 | /// feeds it the input from the gesture recognizer. 593 | /// 594 | /// The gesture data is converted from absolute coordinates to logical 595 | /// coordinates by this widget. 596 | /// 597 | /// The type `T` specifies the return type of the route with which this gesture 598 | /// detector is associated. 599 | class _CupertinoBackGestureDetector extends StatefulWidget { 600 | const _CupertinoBackGestureDetector({ 601 | Key key, 602 | @required this.enabledCallback, 603 | @required this.onStartPopGesture, 604 | @required this.child, 605 | }) : assert(enabledCallback != null), 606 | assert(onStartPopGesture != null), 607 | assert(child != null), 608 | super(key: key); 609 | 610 | final Widget child; 611 | 612 | final ValueGetter enabledCallback; 613 | 614 | final ValueGetter<_CupertinoBackGestureController> onStartPopGesture; 615 | 616 | @override 617 | _CupertinoBackGestureDetectorState createState() => 618 | _CupertinoBackGestureDetectorState(); 619 | } 620 | 621 | class _CupertinoBackGestureDetectorState 622 | extends State<_CupertinoBackGestureDetector> { 623 | _CupertinoBackGestureController _backGestureController; 624 | 625 | HorizontalDragGestureRecognizer _recognizer; 626 | 627 | @override 628 | void initState() { 629 | super.initState(); 630 | _recognizer = HorizontalDragGestureRecognizer(debugOwner: this) 631 | ..onStart = _handleDragStart 632 | ..onUpdate = _handleDragUpdate 633 | ..onEnd = _handleDragEnd 634 | ..onCancel = _handleDragCancel; 635 | } 636 | 637 | @override 638 | void dispose() { 639 | _recognizer.dispose(); 640 | super.dispose(); 641 | } 642 | 643 | void _handleDragStart(DragStartDetails details) { 644 | assert(mounted); 645 | assert(_backGestureController == null); 646 | _backGestureController = widget.onStartPopGesture(); 647 | } 648 | 649 | void _handleDragUpdate(DragUpdateDetails details) { 650 | assert(mounted); 651 | assert(_backGestureController != null); 652 | _backGestureController.dragUpdate( 653 | _convertToLogical(details.primaryDelta / context.size.width)); 654 | } 655 | 656 | void _handleDragEnd(DragEndDetails details) { 657 | assert(mounted); 658 | assert(_backGestureController != null); 659 | _backGestureController.dragEnd(_convertToLogical( 660 | details.velocity.pixelsPerSecond.dx / context.size.width)); 661 | _backGestureController = null; 662 | } 663 | 664 | void _handleDragCancel() { 665 | assert(mounted); 666 | // This can be called even if start is not called, paired with the "down" event 667 | // that we don't consider here. 668 | _backGestureController?.dragEnd(0.0); 669 | _backGestureController = null; 670 | } 671 | 672 | void _handlePointerDown(PointerDownEvent event) { 673 | if (widget.enabledCallback()) _recognizer.addPointer(event); 674 | } 675 | 676 | double _convertToLogical(double value) { 677 | switch (Directionality.of(context)) { 678 | case TextDirection.rtl: 679 | return -value; 680 | case TextDirection.ltr: 681 | return value; 682 | } 683 | return null; 684 | } 685 | 686 | @override 687 | Widget build(BuildContext context) { 688 | assert(debugCheckHasDirectionality(context)); 689 | // For devices with notches, the drag area needs to be larger on the side 690 | // that has the notch. 691 | double dragAreaWidth = Directionality.of(context) == TextDirection.ltr 692 | ? MediaQuery.of(context).padding.left 693 | : MediaQuery.of(context).padding.right; 694 | dragAreaWidth = max(dragAreaWidth, _kBackGestureWidth); 695 | return Stack( 696 | fit: StackFit.passthrough, 697 | children: [ 698 | widget.child, 699 | PositionedDirectional( 700 | start: 0.0, 701 | width: dragAreaWidth, 702 | top: 0.0, 703 | bottom: 0.0, 704 | child: Listener( 705 | onPointerDown: _handlePointerDown, 706 | behavior: HitTestBehavior.translucent, 707 | ), 708 | ), 709 | ], 710 | ); 711 | } 712 | } 713 | 714 | /// A controller for an iOS-style back gesture. 715 | /// 716 | /// This is created by a [CupertinoPageRoute] in response from a gesture caught 717 | /// by a [_CupertinoBackGestureDetector] widget, which then also feeds it input 718 | /// from the gesture. It controls the animation controller owned by the route, 719 | /// based on the input provided by the gesture detector. 720 | /// 721 | /// This class works entirely in logical coordinates (0.0 is new page dismissed, 722 | /// 1.0 is new page on top). 723 | /// 724 | /// The type `T` specifies the return type of the route with which this gesture 725 | /// detector controller is associated. 726 | class _CupertinoBackGestureController { 727 | /// Creates a controller for an iOS-style back gesture. 728 | /// 729 | /// The [navigator] and [controller] arguments must not be null. 730 | _CupertinoBackGestureController({ 731 | @required this.navigator, 732 | @required this.controller, 733 | }) : assert(navigator != null), 734 | assert(controller != null) { 735 | navigator.didStartUserGesture(); 736 | } 737 | 738 | final AnimationController controller; 739 | final NavigatorState navigator; 740 | 741 | /// The drag gesture has changed by [fractionalDelta]. The total range of the 742 | /// drag should be 0.0 to 1.0. 743 | void dragUpdate(double delta) { 744 | controller.value -= delta; 745 | } 746 | 747 | /// The drag gesture has ended with a horizontal motion of 748 | /// [fractionalVelocity] as a fraction of screen width per second. 749 | void dragEnd(double velocity) { 750 | // Fling in the appropriate direction. 751 | // AnimationController.fling is guaranteed to 752 | // take at least one frame. 753 | // 754 | // This curve has been determined through rigorously eyeballing native iOS 755 | // animations. 756 | const Curve animationCurve = Curves.fastLinearToSlowEaseIn; 757 | bool animateForward; 758 | 759 | // If the user releases the page before mid screen with sufficient velocity, 760 | // or after mid screen, we should animate the page out. Otherwise, the page 761 | // should be animated back in. 762 | if (velocity.abs() >= _kMinFlingVelocity) 763 | animateForward = velocity <= 0; 764 | else 765 | animateForward = controller.value > 0.5; 766 | 767 | if (animateForward) { 768 | // The closer the panel is to dismissing, the shorter the animation is. 769 | // We want to cap the animation time, but we want to use a linear curve 770 | // to determine it. 771 | final int droppedPageForwardAnimationTime = min( 772 | lerpDouble( 773 | _kMaxDroppedSwipePageForwardAnimationTime, 0, controller.value) 774 | .floor(), 775 | _kMaxPageBackAnimationTime, 776 | ); 777 | controller.animateTo(1.0, 778 | duration: Duration(milliseconds: droppedPageForwardAnimationTime), 779 | curve: animationCurve); 780 | } else { 781 | // This route is destined to pop at this point. Reuse navigator's pop. 782 | navigator.pop(); 783 | 784 | // The popping may have finished inline if already at the target destination. 785 | if (controller.isAnimating) { 786 | // Otherwise, use a custom popping animation duration and curve. 787 | final int droppedPageBackAnimationTime = lerpDouble( 788 | 0, _kMaxDroppedSwipePageForwardAnimationTime, controller.value) 789 | .floor(); 790 | controller.animateBack(0.0, 791 | duration: Duration(milliseconds: droppedPageBackAnimationTime), 792 | curve: animationCurve); 793 | } 794 | } 795 | 796 | if (controller.isAnimating) { 797 | // Keep the userGestureInProgress in true state so we don't change the 798 | // curve of the page transition mid-flight since CupertinoPageTransition 799 | // depends on userGestureInProgress. 800 | AnimationStatusListener animationStatusCallback; 801 | animationStatusCallback = (AnimationStatus status) { 802 | navigator.didStopUserGesture(); 803 | controller.removeStatusListener(animationStatusCallback); 804 | }; 805 | controller.addStatusListener(animationStatusCallback); 806 | } else { 807 | navigator.didStopUserGesture(); 808 | } 809 | } 810 | } 811 | 812 | // A custom [Decoration] used to paint an extra shadow on the start edge of the 813 | // box it's decorating. It's like a [BoxDecoration] with only a gradient except 814 | // it paints on the start side of the box instead of behind the box. 815 | // 816 | // The [edgeGradient] will be given a [TextDirection] when its shader is 817 | // created, and so can be direction-sensitive; in this file we set it to a 818 | // gradient that uses an AlignmentDirectional to position the gradient on the 819 | // end edge of the gradient's box (which will be the edge adjacent to the start 820 | // edge of the actual box we're supposed to paint in). 821 | class _CupertinoEdgeShadowDecoration extends Decoration { 822 | const _CupertinoEdgeShadowDecoration({this.edgeGradient}); 823 | 824 | // An edge shadow decoration where the shadow is null. This is used 825 | // for interpolating from no shadow. 826 | static const _CupertinoEdgeShadowDecoration none = 827 | _CupertinoEdgeShadowDecoration(); 828 | 829 | // A gradient to draw to the left of the box being decorated. 830 | // Alignments are relative to the original box translated one box 831 | // width to the left. 832 | final LinearGradient edgeGradient; 833 | 834 | // Linearly interpolate between two edge shadow decorations decorations. 835 | // 836 | // The `t` argument represents position on the timeline, with 0.0 meaning 837 | // that the interpolation has not started, returning `a` (or something 838 | // equivalent to `a`), 1.0 meaning that the interpolation has finished, 839 | // returning `b` (or something equivalent to `b`), and values in between 840 | // meaning that the interpolation is at the relevant point on the timeline 841 | // between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and 842 | // 1.0, so negative values and values greater than 1.0 are valid (and can 843 | // easily be generated by curves such as [Curves.elasticInOut]). 844 | // 845 | // Values for `t` are usually obtained from an [Animation], such as 846 | // an [AnimationController]. 847 | // 848 | // See also: 849 | // 850 | // * [Decoration.lerp]. 851 | static _CupertinoEdgeShadowDecoration lerp( 852 | _CupertinoEdgeShadowDecoration a, 853 | _CupertinoEdgeShadowDecoration b, 854 | double t, 855 | ) { 856 | assert(t != null); 857 | if (a == null && b == null) return null; 858 | return _CupertinoEdgeShadowDecoration( 859 | edgeGradient: LinearGradient.lerp(a?.edgeGradient, b?.edgeGradient, t), 860 | ); 861 | } 862 | 863 | @override 864 | _CupertinoEdgeShadowDecoration lerpFrom(Decoration a, double t) { 865 | if (a is _CupertinoEdgeShadowDecoration) 866 | return _CupertinoEdgeShadowDecoration.lerp(a, this, t); 867 | return _CupertinoEdgeShadowDecoration.lerp(null, this, t); 868 | } 869 | 870 | @override 871 | _CupertinoEdgeShadowDecoration lerpTo(Decoration b, double t) { 872 | if (b is _CupertinoEdgeShadowDecoration) 873 | return _CupertinoEdgeShadowDecoration.lerp(this, b, t); 874 | return _CupertinoEdgeShadowDecoration.lerp(this, null, t); 875 | } 876 | 877 | @override 878 | _CupertinoEdgeShadowPainter createBoxPainter([VoidCallback onChanged]) { 879 | return _CupertinoEdgeShadowPainter(this, onChanged); 880 | } 881 | 882 | @override 883 | bool operator ==(Object other) { 884 | if (other.runtimeType != runtimeType) return false; 885 | return other is _CupertinoEdgeShadowDecoration && 886 | other.edgeGradient == edgeGradient; 887 | } 888 | 889 | @override 890 | int get hashCode => edgeGradient.hashCode; 891 | 892 | @override 893 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 894 | super.debugFillProperties(properties); 895 | properties 896 | .add(DiagnosticsProperty('edgeGradient', edgeGradient)); 897 | } 898 | } 899 | 900 | /// A [BoxPainter] used to draw the page transition shadow using gradients. 901 | class _CupertinoEdgeShadowPainter extends BoxPainter { 902 | _CupertinoEdgeShadowPainter( 903 | this._decoration, 904 | VoidCallback onChange, 905 | ) : assert(_decoration != null), 906 | super(onChange); 907 | 908 | final _CupertinoEdgeShadowDecoration _decoration; 909 | 910 | @override 911 | void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { 912 | final LinearGradient gradient = _decoration.edgeGradient; 913 | if (gradient == null) return; 914 | // The drawable space for the gradient is a rect with the same size as 915 | // its parent box one box width on the start side of the box. 916 | final TextDirection textDirection = configuration.textDirection; 917 | assert(textDirection != null); 918 | double deltaX; 919 | switch (textDirection) { 920 | case TextDirection.rtl: 921 | deltaX = configuration.size.width; 922 | break; 923 | case TextDirection.ltr: 924 | deltaX = -configuration.size.width; 925 | break; 926 | } 927 | final Rect rect = (offset & configuration.size).translate(deltaX, 0.0); 928 | final Paint paint = Paint() 929 | ..shader = gradient.createShader(rect, textDirection: textDirection); 930 | 931 | canvas.drawRect(rect, paint); 932 | } 933 | } 934 | 935 | class _CupertinoModalPopupRoute extends PopupRoute { 936 | _CupertinoModalPopupRoute({ 937 | this.barrierColor, 938 | this.barrierLabel, 939 | this.builder, 940 | bool semanticsDismissible, 941 | ImageFilter filter, 942 | RouteSettings settings, 943 | }) : super( 944 | filter: filter, 945 | settings: settings, 946 | ) { 947 | _semanticsDismissible = semanticsDismissible; 948 | } 949 | 950 | final WidgetBuilder builder; 951 | bool _semanticsDismissible; 952 | 953 | @override 954 | final String barrierLabel; 955 | 956 | @override 957 | final Color barrierColor; 958 | 959 | @override 960 | bool get barrierDismissible => true; 961 | 962 | @override 963 | bool get semanticsDismissible => _semanticsDismissible ?? false; 964 | 965 | @override 966 | Duration get transitionDuration => _kModalPopupTransitionDuration; 967 | 968 | Animation _animation; 969 | 970 | Tween _offsetTween; 971 | 972 | @override 973 | Animation createAnimation() { 974 | assert(_animation == null); 975 | _animation = CurvedAnimation( 976 | parent: super.createAnimation(), 977 | 978 | // These curves were initially measured from native iOS horizontal page 979 | // route animations and seemed to be a good match here as well. 980 | curve: Curves.linearToEaseOut, 981 | reverseCurve: Curves.linearToEaseOut.flipped, 982 | ); 983 | _offsetTween = Tween( 984 | begin: const Offset(0.0, 1.0), 985 | end: const Offset(0.0, 0.0), 986 | ); 987 | return _animation; 988 | } 989 | 990 | @override 991 | Widget buildPage(BuildContext context, Animation animation, 992 | Animation secondaryAnimation) { 993 | return CupertinoUserInterfaceLevel( 994 | data: CupertinoUserInterfaceLevelData.elevated, 995 | child: Builder(builder: builder), 996 | ); 997 | } 998 | 999 | @override 1000 | Widget buildTransitions(BuildContext context, Animation animation, 1001 | Animation secondaryAnimation, Widget child) { 1002 | return Align( 1003 | alignment: Alignment.bottomCenter, 1004 | child: FractionalTranslation( 1005 | translation: _offsetTween.evaluate(_animation), 1006 | child: child, 1007 | ), 1008 | ); 1009 | } 1010 | } 1011 | 1012 | /// Shows a modal iOS-style popup that slides up from the bottom of the screen. 1013 | /// 1014 | /// Such a popup is an alternative to a menu or a dialog and prevents the user 1015 | /// from interacting with the rest of the app. 1016 | /// 1017 | /// The `context` argument is used to look up the [Navigator] for the popup. 1018 | /// It is only used when the method is called. Its corresponding widget can be 1019 | /// safely removed from the tree before the popup is closed. 1020 | /// 1021 | /// The `useRootNavigator` argument is used to determine whether to push the 1022 | /// popup to the [Navigator] furthest from or nearest to the given `context`. It 1023 | /// is `false` by default. 1024 | /// 1025 | /// The `semanticsDismissible` argument is used to determine whether the 1026 | /// semantics of the modal barrier are included in the semantics tree. 1027 | /// 1028 | /// The `builder` argument typically builds a [CupertinoActionSheet] widget. 1029 | /// Content below the widget is dimmed with a [ModalBarrier]. The widget built 1030 | /// by the `builder` does not share a context with the location that 1031 | /// `showCupertinoModalPopup` is originally called from. Use a 1032 | /// [StatefulBuilder] or a custom [StatefulWidget] if the widget needs to 1033 | /// update dynamically. 1034 | /// 1035 | /// Returns a `Future` that resolves to the value that was passed to 1036 | /// [Navigator.pop] when the popup was closed. 1037 | /// 1038 | /// See also: 1039 | /// 1040 | /// * [CupertinoActionSheet], which is the widget usually returned by the 1041 | /// `builder` argument to [showCupertinoModalPopup]. 1042 | /// * 1043 | Future showCupertinoModalPopup({ 1044 | @required BuildContext context, 1045 | @required WidgetBuilder builder, 1046 | ImageFilter filter, 1047 | bool useRootNavigator = true, 1048 | bool semanticsDismissible, 1049 | }) { 1050 | assert(useRootNavigator != null); 1051 | return Navigator.of(context, rootNavigator: useRootNavigator).push( 1052 | _CupertinoModalPopupRoute( 1053 | barrierColor: CupertinoDynamicColor.resolve(_kModalBarrierColor, context), 1054 | barrierLabel: 'Dismiss', 1055 | builder: builder, 1056 | filter: filter, 1057 | semanticsDismissible: semanticsDismissible, 1058 | ), 1059 | ); 1060 | } 1061 | 1062 | // The curve and initial scale values were mostly eyeballed from iOS, however 1063 | // they reuse the same animation curve that was modeled after native page 1064 | // transitions. 1065 | final Animatable _dialogScaleTween = Tween(begin: 1.3, end: 1.0) 1066 | .chain(CurveTween(curve: Curves.linearToEaseOut)); 1067 | 1068 | Widget _buildCupertinoDialogTransitions( 1069 | BuildContext context, 1070 | Animation animation, 1071 | Animation secondaryAnimation, 1072 | Widget child) { 1073 | final CurvedAnimation fadeAnimation = CurvedAnimation( 1074 | parent: animation, 1075 | curve: Curves.easeInOut, 1076 | ); 1077 | if (animation.status == AnimationStatus.reverse) { 1078 | return FadeTransition( 1079 | opacity: fadeAnimation, 1080 | child: child, 1081 | ); 1082 | } 1083 | return FadeTransition( 1084 | opacity: fadeAnimation, 1085 | child: ScaleTransition( 1086 | child: child, 1087 | scale: animation.drive(_dialogScaleTween), 1088 | ), 1089 | ); 1090 | } 1091 | 1092 | /// Displays an iOS-style dialog above the current contents of the app, with 1093 | /// iOS-style entrance and exit animations, modal barrier color, and modal 1094 | /// barrier behavior (by default, the dialog is not dismissible with a tap on 1095 | /// the barrier). 1096 | /// 1097 | /// This function takes a `builder` which typically builds a [CupertinoDialog] 1098 | /// or [CupertinoAlertDialog] widget. Content below the dialog is dimmed with a 1099 | /// [ModalBarrier]. The widget returned by the `builder` does not share a 1100 | /// context with the location that `showCupertinoDialog` is originally called 1101 | /// from. Use a [StatefulBuilder] or a custom [StatefulWidget] if the dialog 1102 | /// needs to update dynamically. 1103 | /// 1104 | /// The `context` argument is used to look up the [Navigator] for the dialog. 1105 | /// It is only used when the method is called. Its corresponding widget can 1106 | /// be safely removed from the tree before the dialog is closed. 1107 | /// 1108 | /// The `useRootNavigator` argument is used to determine whether to push the 1109 | /// dialog to the [Navigator] furthest from or nearest to the given `context`. 1110 | /// By default, `useRootNavigator` is `true` and the dialog route created by 1111 | /// this method is pushed to the root navigator. 1112 | /// 1113 | /// If the application has multiple [Navigator] objects, it may be necessary to 1114 | /// call `Navigator.of(context, rootNavigator: true).pop(result)` to close the 1115 | /// dialog rather than just `Navigator.pop(context, result)`. 1116 | /// 1117 | /// Returns a [Future] that resolves to the value (if any) that was passed to 1118 | /// [Navigator.pop] when the dialog was closed. 1119 | /// 1120 | /// See also: 1121 | /// 1122 | /// * [CupertinoDialog], an iOS-style dialog. 1123 | /// * [CupertinoAlertDialog], an iOS-style alert dialog. 1124 | /// * [showDialog], which displays a Material-style dialog. 1125 | /// * [showGeneralDialog], which allows for customization of the dialog popup. 1126 | /// * 1127 | Future showCupertinoDialog({ 1128 | @required BuildContext context, 1129 | @required WidgetBuilder builder, 1130 | bool useRootNavigator = true, 1131 | bool barrierDismissible = false, 1132 | RouteSettings routeSettings, 1133 | }) { 1134 | assert(builder != null); 1135 | assert(useRootNavigator != null); 1136 | return showGeneralDialog( 1137 | context: context, 1138 | barrierDismissible: barrierDismissible, 1139 | barrierLabel: CupertinoLocalizations.of(context).modalBarrierDismissLabel, 1140 | barrierColor: CupertinoDynamicColor.resolve(_kModalBarrierColor, context), 1141 | // This transition duration was eyeballed comparing with iOS 1142 | transitionDuration: const Duration(milliseconds: 250), 1143 | pageBuilder: (BuildContext context, Animation animation, 1144 | Animation secondaryAnimation) { 1145 | return builder(context); 1146 | }, 1147 | transitionBuilder: _buildCupertinoDialogTransitions, 1148 | useRootNavigator: useRootNavigator, 1149 | routeSettings: routeSettings, 1150 | ); 1151 | } 1152 | --------------------------------------------------------------------------------