├── .github └── workflows │ └── dart.yml ├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── README.md ├── android ├── .gitignore ├── .project ├── .settings │ └── org.eclipse.buildship.core.prefs ├── app │ ├── .classpath │ ├── .project │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── zubisofts │ │ │ │ └── moviedb │ │ │ │ └── MainActivity.java │ │ └── res │ │ │ ├── drawable │ │ │ ├── icon.png │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── provider_paths.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── settings_aar.gradle ├── assets ├── fonts │ ├── Merriweather-Italic.ttf │ ├── Merriweather-Regular.ttf │ ├── Poppins-Bold.ttf │ ├── Poppins-ExtraLight.otf │ ├── Poppins-Light.otf │ ├── Poppins-Medium.ttf │ ├── Poppins-Regular.otf │ ├── Poppins-SemiBold.otf │ ├── Roboto-Bold.ttf │ ├── Roboto-Italic.ttf │ └── Roboto-Regular.ttf └── images │ ├── app_logo.png │ └── no-image.jpg ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── bloc │ ├── app_bloc │ │ ├── app_bloc.dart │ │ ├── app_event.dart │ │ └── app_state.dart │ ├── auth_bloc │ │ ├── auth_bloc.dart │ │ ├── auth_event.dart │ │ ├── auth_state.dart │ │ └── bloc.dart │ ├── movies_bloc │ │ ├── bloc.dart │ │ ├── movies_bloc.dart │ │ ├── movies_event.dart │ │ └── movies_state.dart │ └── tv_bloc │ │ ├── tv_bloc.dart │ │ ├── tv_event.dart │ │ └── tv_state.dart ├── fragments │ ├── discover_list.dart │ ├── discover_page.dart │ ├── movies_category.dart │ ├── news_page.dart │ └── tv_category.dart ├── main.dart ├── model │ ├── credit.dart │ ├── favourite.dart │ ├── genre.dart │ ├── movie.dart │ ├── movie_details.dart │ ├── movie_images.dart │ ├── movie_list.dart │ ├── movie_review.dart │ ├── person.dart │ ├── person_images.dart │ ├── tv_details.dart │ ├── tv_list_model.dart │ ├── tv_season_details.dart │ ├── user.dart │ └── video_details.dart ├── pages │ ├── auth_page.dart │ ├── episode_details_page.dart │ ├── favourites_page.dart │ ├── home.dart │ ├── image_slide_screen.dart │ ├── loading_text_widget.dart │ ├── movie_credits_page.dart │ ├── movie_detail_page.dart │ ├── movie_list_page.dart │ ├── person_details_page.dart │ ├── profile_screen.dart │ ├── sign_in_page.dart │ ├── sign_up_page.dart │ ├── tv_detail_page.dart │ ├── tv_list_page.dart │ ├── tv_season_detail_page.dart │ ├── tv_seasons_page.dart │ └── watchlist_page.dart ├── repository │ ├── auth_repository.dart │ ├── constants.dart │ ├── movie_repository.dart │ └── tv_series_repository.dart ├── widgets │ ├── auth_modal_form.dart │ ├── cast_item.dart │ ├── cast_list.dart │ ├── crew_item.dart │ ├── curved_path_bg.dart │ ├── custom_path_clipper.dart │ ├── image_list_row.dart │ ├── loading_button.dart │ ├── movie_item_horizontal.dart │ ├── movie_item_vertical.dart │ ├── movie_list_row.dart │ ├── movie_review_widget.dart │ ├── search_widget.dart │ ├── trailers_row_widget.dart │ ├── tv_item_horizontal.dart │ ├── tv_last_episode_widget.dart │ └── tv_list_row.dart └── wrapper.dart ├── privacy_policy.html ├── pubspec.lock ├── pubspec.yaml └── screenshots ├── Screenshot_20200328-060258.png ├── Screenshot_20200328-060307.png ├── Screenshot_20200328-060319.png ├── Screenshot_20200328-060328.png ├── Screenshot_20200328-060411.png ├── Screenshot_20200328-060427.png ├── Screenshot_20200328-060437.png ├── Screenshot_20200328-060450.png ├── Screenshot_20200328-060550.png ├── Screenshot_20200328-060618.png ├── Screenshot_20200328-060637.png └── Screenshot_20200328-060646.png /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: Dart 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | pull_request: 12 | branches: [ master ] 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603 22 | 23 | - name: Flutter action 24 | 25 | uses: subosito/flutter-action@v1.5.3 26 | with: 27 | flutter-version: 2.2.3 28 | channel: stable 29 | - name: Install dependencies 30 | run: flutter pub get 31 | - name: Build Flutter apk 32 | run: flutter build apk 33 | 34 | # - name: Analyze project source 35 | # run: flutter analyze 36 | 37 | # - name: Run tests 38 | # run: flutter test 39 | 40 | -------------------------------------------------------------------------------- /.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 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # android/app/google-services.json 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Exceptions to above rules. 39 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 40 | 41 | # key.properties 42 | # app/upload-keystore.jks -------------------------------------------------------------------------------- /.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: 27321ebbad34b0a3fafe99fac037102196d655ff 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flutter", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A flutter Movie App (MovieDB) using TMDB API 2 | 3 | This app consumes the TMDB API to showcase movies and TV shows. 4 | API: https://developers.themoviedb.org 5 | ![Screenshot1](/screenshots/Screenshot_20200328-060258.png) 6 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /android/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=.. 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | def keystoreProperties = new Properties() 28 | def keystorePropertiesFile = rootProject.file('key.properties') 29 | if (keystorePropertiesFile.exists()) { 30 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 31 | } 32 | 33 | 34 | android { 35 | compileSdkVersion 30 36 | 37 | lintOptions { 38 | disable 'InvalidPackage' 39 | } 40 | 41 | defaultConfig { 42 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 43 | applicationId "com.zubisofts.moviedb" 44 | minSdkVersion 16 45 | targetSdkVersion 30 46 | versionCode flutterVersionCode.toInteger() 47 | versionName flutterVersionName 48 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 49 | multiDexEnabled true 50 | } 51 | 52 | signingConfigs { 53 | release { 54 | keyAlias keystoreProperties['keyAlias'] 55 | keyPassword keystoreProperties['keyPassword'] 56 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 57 | storePassword keystoreProperties['storePassword'] 58 | } 59 | } 60 | buildTypes { 61 | release { 62 | signingConfig signingConfigs.release 63 | } 64 | } 65 | 66 | } 67 | 68 | flutter { 69 | source '../..' 70 | } 71 | 72 | dependencies { 73 | testImplementation 'junit:junit:4.12' 74 | androidTestImplementation 'androidx.test:runner:1.1.1' 75 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 76 | implementation 'com.android.support:multidex:1.0.3' 77 | } 78 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 10 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 31 | 32 | 37 | 40 | 41 | 43 | 44 | 48 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/zubisofts/moviedb/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zubisofts.moviedb; 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity; 5 | import io.flutter.embedding.engine.FlutterEngine; 6 | import io.flutter.plugins.GeneratedPluginRegistrant; 7 | 8 | public class MainActivity extends FlutterActivity { 9 | @Override 10 | public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { 11 | GeneratedPluginRegistrant.registerWith(flutterEngine); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/android/app/src/main/res/drawable/icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MovieDB 4 | 622178651684324 5 | fb622178651684324 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:4.1.3' 9 | classpath 'com.google.gms:google-services:4.3.2' 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | jcenter() 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | org.gradle.jvmargs=-Xmx1536M 3 | android.enableR8=true 4 | android.useAndroidX=true 5 | android.enableJetifier=true 6 | -------------------------------------------------------------------------------- /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-7.0.2-bin.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /assets/fonts/Merriweather-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Merriweather-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Merriweather-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Merriweather-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Poppins-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins-ExtraLight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Poppins-ExtraLight.otf -------------------------------------------------------------------------------- /assets/fonts/Poppins-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Poppins-Light.otf -------------------------------------------------------------------------------- /assets/fonts/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Poppins-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Poppins-Regular.otf -------------------------------------------------------------------------------- /assets/fonts/Poppins-SemiBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Poppins-SemiBold.otf -------------------------------------------------------------------------------- /assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /assets/images/app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/images/app_logo.png -------------------------------------------------------------------------------- /assets/images/no-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/assets/images/no-image.jpg -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/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/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | MovieDB 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 | io.flutter.embedded_views_preview 45 | 46 | NSPhotoLibraryAddUsageDescription 47 | TODO 48 | NSPhotoLibraryUsageDescription 49 | TODO 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/bloc/app_bloc/app_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | 7 | import 'package:MovieDB/repository/constants.dart'; 8 | 9 | part 'app_event.dart'; 10 | part 'app_state.dart'; 11 | 12 | class AppBloc extends Bloc { 13 | 14 | AppBloc() : super(AppInitial()){ 15 | _getThemeValueEventToState(); 16 | } 17 | 18 | 19 | @override 20 | Stream mapEventToState( 21 | AppEvent event, 22 | ) async* { 23 | 24 | // Theme event and state 25 | if (event is ChangeThemeEvent) { 26 | yield* _setThemeEventToState(event.value); 27 | } 28 | 29 | if (event is GetThemeValueEvent) { 30 | yield* _getThemeValueEventToState(); 31 | } 32 | 33 | if(event is ListScrollEvent){ 34 | yield ListScrollState(load: !event.load); 35 | } 36 | 37 | // List toggle events and states 38 | if(event is ListToggleEvent){ 39 | // print(event.isVertical); 40 | yield ListToggleState(isVertical: event.isVertical); 41 | } 42 | } 43 | 44 | Stream _setThemeEventToState(bool value) async* { 45 | final pref = await SharedPreferences.getInstance(); 46 | var x = await pref.setBool(THEME_PREF_KEY, value); 47 | 48 | 49 | if (x) { 50 | var themeValue =pref.getBool(THEME_PREF_KEY); 51 | yield ThemeChangedState(value: themeValue); 52 | // print("Theme set:$themeValue"); 53 | // yield GetThemeValueState(value: x); 54 | } 55 | } 56 | 57 | Stream _getThemeValueEventToState() async* { 58 | final pref = await SharedPreferences.getInstance(); 59 | 60 | var x = pref.getBool(THEME_PREF_KEY) ?? false; 61 | // print("Fetched theme:$x"); 62 | 63 | yield GetThemeValueState(value: x); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/bloc/app_bloc/app_event.dart: -------------------------------------------------------------------------------- 1 | part of 'app_bloc.dart'; 2 | 3 | abstract class AppEvent extends Equatable { 4 | const AppEvent(); 5 | } 6 | 7 | class ChangeThemeEvent extends AppEvent { 8 | final bool value; 9 | ChangeThemeEvent({ 10 | this.value, 11 | }); 12 | 13 | @override 14 | List get props => [value]; 15 | } 16 | 17 | class GetThemeValueEvent extends AppEvent { 18 | GetThemeValueEvent(); 19 | 20 | @override 21 | List get props => []; 22 | } 23 | 24 | class ListToggleEvent extends AppEvent { 25 | final bool isVertical; 26 | ListToggleEvent({ 27 | this.isVertical, 28 | }); 29 | @override 30 | List get props => [isVertical]; 31 | } 32 | 33 | class ListScrollEvent extends AppEvent { 34 | final bool load; 35 | ListScrollEvent({ 36 | this.load, 37 | }); 38 | @override 39 | List get props => [load]; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /lib/bloc/app_bloc/app_state.dart: -------------------------------------------------------------------------------- 1 | part of 'app_bloc.dart'; 2 | 3 | abstract class AppState extends Equatable { 4 | const AppState(); 5 | } 6 | 7 | class AppInitial extends AppState { 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class ThemeChangedState extends AppState { 13 | final bool value; 14 | ThemeChangedState({ 15 | this.value, 16 | }); 17 | 18 | @override 19 | List get props => [value]; 20 | } 21 | 22 | class GetThemeValueState extends AppState { 23 | final bool value; 24 | GetThemeValueState({ 25 | this.value, 26 | }); 27 | 28 | @override 29 | List get props => [value]; 30 | } 31 | 32 | class ListToggleState extends AppState { 33 | final bool isVertical; 34 | ListToggleState({ 35 | this.isVertical, 36 | }); 37 | @override 38 | List get props => [isVertical]; 39 | } 40 | 41 | class ListScrollState extends AppState { 42 | 43 | final bool load; 44 | ListScrollState({ 45 | this.load, 46 | }); 47 | 48 | @override 49 | List get props => [load]; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /lib/bloc/auth_bloc/auth_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:MovieDB/model/user.dart'; 4 | import 'package:MovieDB/repository/auth_repository.dart'; 5 | import './bloc.dart'; 6 | 7 | class AuthBloc extends Bloc { 8 | AuthRepository respository = AuthRepository(); 9 | String email = ""; 10 | String password = ""; 11 | String cpassword = ""; 12 | String fname = ""; 13 | String lname = ""; 14 | 15 | StreamSubscription _authSubscription; 16 | 17 | AuthBloc() : super(InitialAuthState()); 18 | 19 | @override 20 | Stream mapEventToState( 21 | AuthEvent event, 22 | ) async* { 23 | if (event is LoginEvent) { 24 | yield* processLoginState(); 25 | } 26 | 27 | if (event is GoogleLoginEvent) { 28 | yield* _mapGoogleLoggedState(); 29 | } 30 | 31 | if (event is FacebookLoginEvent) { 32 | yield* _mapFacebookLoggedState(); 33 | } 34 | 35 | if (event is OnEmailChangeEvent) { 36 | email = event.email; 37 | } 38 | 39 | if (event is OnPasswordChangeEvent) { 40 | password = event.password; 41 | } 42 | 43 | if (event is OnRegDetailsChangedEvent) { 44 | email = event.email; 45 | fname = event.firstname; 46 | lname = event.lastname; 47 | password = event.password; 48 | cpassword = event.confirmPassword; 49 | } 50 | 51 | if (event is RegisterEvent) { 52 | if (email.length < 1) { 53 | yield AuthErrorState(error: "Email address should not be empty"); 54 | } else if (fname.length < 1) { 55 | yield AuthErrorState(error: "Firstname should not be empty"); 56 | } else if (lname.length < 1) { 57 | yield AuthErrorState(error: "Lastname must not be empty"); 58 | } else if (password != cpassword) { 59 | yield AuthErrorState(error: "Password do not match."); 60 | } else { 61 | // yield LoadingState(); 62 | yield* registerUser(); 63 | } 64 | } 65 | if (event is AuthStateChangedEvent) { 66 | yield* _mapAuthStateChangedState(event.user); 67 | } 68 | if (event is ListenToLoginEvent) { 69 | yield* _mapLoggedState(); 70 | } 71 | 72 | if (event is LogoutEvent) { 73 | yield* _mapLogoutState(); 74 | } 75 | } 76 | 77 | Stream processLoginState() async* { 78 | yield AuthLoadingState(); 79 | 80 | dynamic user = await respository.loginUser(email, password); 81 | 82 | if (user is String) { 83 | yield AuthErrorState(error: user); 84 | } else { 85 | yield LoggedInState(user: user); 86 | add(AuthStateChangedEvent(user: user)); 87 | } 88 | } 89 | 90 | Stream registerUser() async* { 91 | yield AuthLoadingState(); 92 | 93 | dynamic data = 94 | await respository.registerUser(email, fname, lname, password); 95 | 96 | if (data is User) { 97 | yield RegisteredState(user: data); 98 | } else { 99 | yield AuthErrorState(error: data); 100 | } 101 | } 102 | 103 | Stream _mapLoggedState() async* { 104 | _authSubscription?.cancel(); 105 | _authSubscription = respository.listenToSignIn().listen((user) { 106 | // if(user!=null) 107 | add(AuthStateChangedEvent( 108 | user: user != null ? user : null)); 109 | }); 110 | } 111 | 112 | Stream _mapAuthStateChangedState(user) async* { 113 | yield AuthLoginState(user: user); 114 | } 115 | 116 | Stream _mapLogoutState() async* { 117 | await respository.logout(); 118 | } 119 | 120 | Stream _mapGoogleLoggedState() async*{ 121 | yield AuthLoadingState(); 122 | 123 | dynamic user = await respository.loginUserWithCredentials(); 124 | 125 | if (user is String) { 126 | yield AuthErrorState(error: user); 127 | } else { 128 | yield LoggedInState(user: user); 129 | add(AuthStateChangedEvent(user: user)); 130 | } 131 | } 132 | 133 | Stream _mapFacebookLoggedState() async*{ 134 | yield AuthLoadingState(); 135 | 136 | dynamic user = await respository.loginUserWithFBCredentials(); 137 | 138 | if (user is String) { 139 | yield AuthErrorState(error: user); 140 | } else { 141 | yield LoggedInState(user: user); 142 | add(AuthStateChangedEvent(user: user)); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /lib/bloc/auth_bloc/auth_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | 4 | 5 | abstract class AuthEvent extends Equatable { 6 | const AuthEvent(); 7 | } 8 | 9 | class LoginEvent extends AuthEvent { 10 | 11 | @override 12 | List get props => []; 13 | } 14 | 15 | class GoogleLoginEvent extends AuthEvent { 16 | 17 | @override 18 | List get props => []; 19 | } 20 | 21 | class FacebookLoginEvent extends AuthEvent { 22 | 23 | @override 24 | List get props => []; 25 | } 26 | 27 | class Logout extends AuthEvent { 28 | @override 29 | List get props => []; 30 | } 31 | 32 | class RegisterEvent extends AuthEvent { 33 | 34 | @override 35 | List get props => []; 36 | } 37 | 38 | class ListenToLoginEvent extends AuthEvent{ 39 | 40 | @override 41 | List get props => []; 42 | 43 | } 44 | 45 | 46 | class AuthStateChangedEvent extends AuthEvent { 47 | 48 | final User user; 49 | AuthStateChangedEvent({ 50 | this.user, 51 | }); 52 | 53 | @override 54 | List get props => [user]; 55 | 56 | } 57 | 58 | class OnEmailChangeEvent extends AuthEvent { 59 | final String email; 60 | OnEmailChangeEvent({ 61 | this.email, 62 | }); 63 | @override 64 | List get props => [email]; 65 | } 66 | 67 | class OnPasswordChangeEvent extends AuthEvent { 68 | final String password; 69 | OnPasswordChangeEvent({ 70 | this.password, 71 | }); 72 | 73 | @override 74 | List get props => [password]; 75 | } 76 | 77 | class OnRegDetailsChangedEvent extends AuthEvent { 78 | final String firstname, lastname, email, password, confirmPassword; 79 | OnRegDetailsChangedEvent({ 80 | this.firstname, 81 | this.lastname, 82 | this.email, 83 | this.password, 84 | this.confirmPassword, 85 | }); 86 | 87 | @override 88 | List get props => [firstname,lastname,email,password,confirmPassword]; 89 | } 90 | 91 | class LogoutEvent extends AuthEvent{ 92 | @override 93 | List get props => []; 94 | 95 | } 96 | -------------------------------------------------------------------------------- /lib/bloc/auth_bloc/auth_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:MovieDB/bloc/movies_bloc/movies_state.dart'; 4 | 5 | import 'package:MovieDB/model/user.dart' as NUser; 6 | 7 | abstract class AuthState extends Equatable { 8 | const AuthState(); 9 | } 10 | 11 | class InitialAuthState extends AuthState { 12 | @override 13 | List get props => []; 14 | } 15 | 16 | class LoggedInState extends AuthState { 17 | 18 | final User user; 19 | LoggedInState({ 20 | this.user, 21 | }); 22 | 23 | @override 24 | List get props => [user]; 25 | } 26 | 27 | class RegisterState extends AuthState{ 28 | 29 | @override 30 | List get props => []; 31 | 32 | } 33 | 34 | class RegisteredState extends AuthState { 35 | 36 | final NUser.User user; 37 | RegisteredState({ 38 | this.user, 39 | }); 40 | 41 | @override 42 | List get props => [user]; 43 | } 44 | 45 | class AuthLoadingState extends AuthState { 46 | 47 | @override 48 | List get props => []; 49 | } 50 | 51 | class EmailChangeState extends MoviesState{ 52 | 53 | final String email; 54 | EmailChangeState({ 55 | this.email, 56 | }); 57 | 58 | @override 59 | List get props => [email]; 60 | } 61 | 62 | class PasswordChangeState extends AuthState { 63 | 64 | final String password; 65 | PasswordChangeState({ 66 | this.password, 67 | }); 68 | 69 | @override 70 | List get props => [password]; 71 | } 72 | 73 | class AuthErrorState extends AuthState { 74 | 75 | final String error; 76 | AuthErrorState({ 77 | this.error, 78 | }); 79 | 80 | @override 81 | List get props => [error]; 82 | 83 | } 84 | class AuthLoginState extends AuthState { 85 | 86 | final User user; 87 | AuthLoginState({ 88 | this.user, 89 | }); 90 | 91 | @override 92 | List get props => [user]; 93 | 94 | } 95 | -------------------------------------------------------------------------------- /lib/bloc/auth_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'auth_bloc.dart'; 2 | export 'auth_event.dart'; 3 | export 'auth_state.dart'; 4 | -------------------------------------------------------------------------------- /lib/bloc/movies_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'movies_bloc.dart'; 2 | export 'movies_event.dart'; 3 | export 'movies_state.dart'; 4 | -------------------------------------------------------------------------------- /lib/bloc/movies_bloc/movies_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/repository/constants.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | import 'package:MovieDB/model/movie_details.dart'; 5 | import 'package:MovieDB/repository/movie_repository.dart'; 6 | 7 | abstract class MoviesEvent extends Equatable { 8 | const MoviesEvent(); 9 | } 10 | 11 | class LoadingEvent extends MoviesEvent { 12 | @override 13 | List get props => []; 14 | } 15 | 16 | class LoadMoviesEvent extends MoviesEvent { 17 | final MovieCat type; 18 | final int id; 19 | final currentPageIndex; 20 | 21 | LoadMoviesEvent({this.currentPageIndex, this.type, this.id}); 22 | 23 | @override 24 | List get props => [type,id,currentPageIndex]; 25 | } 26 | 27 | class LoadMoreMoviesEvent extends MoviesEvent { 28 | final MovieCat type; 29 | final int id; 30 | final currentPageIndex; 31 | 32 | LoadMoreMoviesEvent({this.currentPageIndex, this.type, this.id}); 33 | 34 | @override 35 | List get props => [type,id,currentPageIndex]; 36 | } 37 | 38 | class LoadMovieDetailsEvent extends MoviesEvent { 39 | final int id; 40 | LoadMovieDetailsEvent({ 41 | this.id, 42 | }); 43 | 44 | @override 45 | List get props => [id]; 46 | } 47 | 48 | class LoadMovieVideosEvent extends MoviesEvent { 49 | final int id; 50 | LoadMovieVideosEvent({ 51 | this.id, 52 | }); 53 | 54 | @override 55 | List get props => [id]; 56 | } 57 | 58 | class PersonDetailsEvent extends MoviesEvent { 59 | final int id; 60 | PersonDetailsEvent({ 61 | this.id, 62 | }); 63 | 64 | @override 65 | List get props => [id]; 66 | } 67 | 68 | class GetPersonImagesEvent extends MoviesEvent { 69 | final int id; 70 | GetPersonImagesEvent({ 71 | this.id, 72 | }); 73 | 74 | @override 75 | List get props => [id]; 76 | } 77 | 78 | 79 | class AddFavouritesEvent extends MoviesEvent { 80 | final details; 81 | final String uid; 82 | final MediaType mediaType; 83 | AddFavouritesEvent({ 84 | this.details, 85 | this.uid, 86 | this.mediaType 87 | }); 88 | 89 | @override 90 | List get props => [details,uid,mediaType]; 91 | } 92 | 93 | class GetFavouriteEvent extends MoviesEvent { 94 | final int id; 95 | final String uid; 96 | final MediaType mediaType; 97 | GetFavouriteEvent({ 98 | this.id, 99 | this.uid, 100 | this.mediaType 101 | }); 102 | 103 | @override 104 | List get props => [id,uid,mediaType]; 105 | } 106 | class GetFavouriteMovieEvent extends MoviesEvent { 107 | final favourite; 108 | GetFavouriteMovieEvent({ 109 | this.favourite, 110 | }); 111 | 112 | @override 113 | List get props => [favourite]; 114 | } 115 | 116 | class LoadFavouriteMoviesEvent extends MoviesEvent { 117 | final String uid; 118 | final MediaType mediaType; 119 | LoadFavouriteMoviesEvent({ 120 | this.uid, 121 | this.mediaType 122 | }); 123 | 124 | @override 125 | List get props => [uid]; 126 | } 127 | 128 | class LoadAllFavouritesMovieEvent extends MoviesEvent { 129 | final List favourites; 130 | LoadAllFavouritesMovieEvent({ 131 | this.favourites, 132 | }); 133 | 134 | @override 135 | List get props => [favourites]; 136 | } 137 | 138 | 139 | class AddWatchListEvent extends MoviesEvent { 140 | final movieDetails; 141 | final String uid; 142 | final MediaType mediaType; 143 | AddWatchListEvent({ 144 | this.movieDetails, 145 | this.uid, 146 | this.mediaType 147 | }); 148 | 149 | @override 150 | List get props => [movieDetails,uid,mediaType]; 151 | } 152 | 153 | class GetWatchListItemEvent extends MoviesEvent { 154 | final int id; 155 | final String uid; 156 | final MediaType mediaType; 157 | GetWatchListItemEvent({ 158 | this.id, 159 | this.uid, 160 | this.mediaType 161 | }); 162 | 163 | @override 164 | List get props => [id,mediaType]; 165 | } 166 | class GetWatchListMovieEvent extends MoviesEvent { 167 | final watchListItem; 168 | GetWatchListMovieEvent({ 169 | this.watchListItem, 170 | }); 171 | 172 | @override 173 | List get props => [watchListItem]; 174 | } 175 | 176 | class LoadWatchListMoviesEvent extends MoviesEvent { 177 | final String uid; 178 | final MediaType mediaType; 179 | LoadWatchListMoviesEvent({ 180 | this.uid, 181 | this.mediaType 182 | }); 183 | 184 | @override 185 | List get props => [uid,mediaType,mediaType]; 186 | } 187 | 188 | class LoadAllWatchListMovieEvent extends MoviesEvent { 189 | final List watchLists; 190 | final MediaType mediaType; 191 | LoadAllWatchListMovieEvent({ 192 | this.watchLists, 193 | this.mediaType 194 | }); 195 | 196 | @override 197 | List get props => [watchLists,mediaType]; 198 | } 199 | 200 | class DeleteFavouriteMovieItem extends MoviesEvent { 201 | final int movieId; 202 | final String uid; 203 | final MediaType mediaType; 204 | DeleteFavouriteMovieItem({ 205 | this.movieId, 206 | this.uid, 207 | this.mediaType 208 | }); 209 | @override 210 | List get props => [movieId,uid,mediaType]; 211 | 212 | } 213 | 214 | class DeleteWatchListMovieItem extends MoviesEvent { 215 | final int movieId; 216 | final String uid; 217 | final MediaType mediaType; 218 | DeleteWatchListMovieItem({ 219 | this.movieId, 220 | this.uid, 221 | this.mediaType 222 | }); 223 | @override 224 | List get props => [movieId,uid,mediaType]; 225 | 226 | } 227 | 228 | class GetMovieReviews extends MoviesEvent { 229 | final int movieId; 230 | GetMovieReviews({ 231 | this.movieId, 232 | }); 233 | @override 234 | List get props => [movieId]; 235 | 236 | } 237 | 238 | class GetGenres extends MoviesEvent { 239 | final MediaType mediaType; 240 | GetGenres({ 241 | this.mediaType, 242 | }); 243 | @override 244 | List get props => [mediaType]; 245 | 246 | } 247 | class Discover extends MoviesEvent { 248 | final MediaType mediaType; 249 | final List genres; 250 | final String sortQuery; 251 | final String year; 252 | Discover({ 253 | this.mediaType, 254 | this.genres, 255 | this.sortQuery, 256 | this.year 257 | }); 258 | @override 259 | List get props => [mediaType,genres,sortQuery,year]; 260 | 261 | } 262 | -------------------------------------------------------------------------------- /lib/bloc/movies_bloc/movies_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/model/genre.dart'; 2 | import 'package:MovieDB/model/movie_review.dart'; 3 | import 'package:MovieDB/repository/constants.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | import 'package:MovieDB/model/credit.dart'; 6 | import 'package:MovieDB/model/favourite.dart'; 7 | import 'package:MovieDB/model/movie_details.dart'; 8 | import 'package:MovieDB/model/movie_list.dart'; 9 | import 'package:MovieDB/model/person.dart'; 10 | import 'package:MovieDB/model/person_images.dart'; 11 | import 'package:MovieDB/model/video_details.dart'; 12 | import 'package:flutter/material.dart'; 13 | 14 | abstract class MoviesState extends Equatable { 15 | const MoviesState(); 16 | } 17 | 18 | class InitialMoviesState extends MoviesState { 19 | @override 20 | List get props => []; 21 | } 22 | 23 | class MovieLoadingState extends MoviesState { 24 | @override 25 | List get props => []; 26 | } 27 | 28 | class LoadingState extends MoviesState { 29 | @override 30 | List get props => []; 31 | } 32 | 33 | class MoviesLoadedState extends MoviesState { 34 | final MovieList movies; 35 | 36 | MoviesLoadedState({ 37 | this.movies, 38 | }); 39 | 40 | @override 41 | List get props => [movies]; 42 | } 43 | 44 | class MoreMoviesLoadedState extends MoviesState { 45 | final MovieList movies; 46 | 47 | MoreMoviesLoadedState({ 48 | this.movies, 49 | }); 50 | 51 | @override 52 | List get props => [movies]; 53 | } 54 | 55 | class MovieDetailsReadyState extends MoviesState { 56 | final MovieDetails movieDetails; 57 | final VideoDetails videoDetails; 58 | final MovieList similarMovies; 59 | final Credit credit; 60 | 61 | MovieDetailsReadyState({ 62 | this.movieDetails, 63 | this.videoDetails, 64 | this.similarMovies, 65 | this.credit, 66 | }); 67 | 68 | @override 69 | List get props => [movieDetails, videoDetails, similarMovies]; 70 | } 71 | 72 | class MovieVideosReadyState extends MoviesState { 73 | final VideoDetails videoDetails; 74 | 75 | MovieVideosReadyState({ 76 | this.videoDetails, 77 | }); 78 | 79 | @override 80 | List get props => [videoDetails]; 81 | } 82 | 83 | class PersonDetialsState extends MoviesState { 84 | final Person person; 85 | 86 | PersonDetialsState({ 87 | this.person, 88 | }); 89 | 90 | @override 91 | List get props => [person]; 92 | } 93 | 94 | class PersonImagesState extends MoviesState { 95 | final PersonImages images; 96 | 97 | PersonImagesState({ 98 | this.images, 99 | }); 100 | 101 | @override 102 | List get props => [images]; 103 | } 104 | 105 | class FavouritesState extends MoviesState { 106 | final List favourites; 107 | 108 | FavouritesState({ 109 | this.favourites, 110 | }); 111 | 112 | @override 113 | List get props => [favourites]; 114 | } 115 | 116 | class FavouriteItemState extends MoviesState { 117 | final favourite; 118 | 119 | FavouriteItemState({ 120 | this.favourite, 121 | }); 122 | 123 | @override 124 | List get props => [favourite]; 125 | } 126 | 127 | class FavouriteMoviesLoaded extends MoviesState { 128 | final List favourites; 129 | 130 | FavouriteMoviesLoaded({ 131 | this.favourites, 132 | }); 133 | 134 | @override 135 | List get props => [favourites]; 136 | } 137 | 138 | // class WatchListState extends MoviesState { 139 | // final List watchList; 140 | // WatchListState({ 141 | // this.watchList, 142 | // }); 143 | 144 | // @override 145 | // List get props => [watchList]; 146 | // } 147 | 148 | class WatchListItem extends MoviesState { 149 | final watchListItem; 150 | 151 | WatchListItem({ 152 | this.watchListItem, 153 | }); 154 | 155 | @override 156 | List get props => [watchListItem]; 157 | } 158 | 159 | class WatchListMoviesLoaded extends MoviesState { 160 | final List watchList; 161 | final MediaType mediaType; 162 | 163 | WatchListMoviesLoaded({ 164 | this.watchList, 165 | this.mediaType 166 | }); 167 | 168 | @override 169 | List get props => [watchList,mediaType]; 170 | } 171 | 172 | class MovieErrorState extends MoviesState { 173 | @override 174 | List get props => []; 175 | } 176 | 177 | class MovieReviewsLoaded extends MoviesState { 178 | final MovieReview movieReview; 179 | 180 | MovieReviewsLoaded({@required this.movieReview}); 181 | 182 | @override 183 | List get props => [movieReview]; 184 | } 185 | class GenresState extends MoviesState { 186 | final Genry genry; 187 | 188 | GenresState({ 189 | this.genry, 190 | }); 191 | 192 | @override 193 | List get props => [genry]; 194 | } 195 | 196 | class DiscoverState extends MoviesState { 197 | final dynamic items; 198 | 199 | DiscoverState({ 200 | this.items, 201 | }); 202 | 203 | @override 204 | List get props => [items]; 205 | } -------------------------------------------------------------------------------- /lib/bloc/tv_bloc/tv_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:MovieDB/model/credit.dart'; 4 | import 'package:MovieDB/model/tv_details.dart'; 5 | import 'package:MovieDB/model/tv_list_model.dart'; 6 | import 'package:MovieDB/model/tv_season_details.dart'; 7 | import 'package:MovieDB/model/video_details.dart'; 8 | import 'package:MovieDB/repository/tv_series_repository.dart'; 9 | import 'package:bloc/bloc.dart'; 10 | import 'package:equatable/equatable.dart'; 11 | 12 | part 'tv_event.dart'; 13 | 14 | part 'tv_state.dart'; 15 | 16 | class TvBloc extends Bloc { 17 | TVRepository tvRepository = TVRepository(); 18 | 19 | TvBloc() : super(TvInitial()); 20 | 21 | @override 22 | Stream mapEventToState( 23 | TvEvent event, 24 | ) async* { 25 | if (event is LoadTvEvent) { 26 | yield* _fetchTvState(event.type, event.id, event.currentPageIndex); 27 | } 28 | 29 | if (event is LoadMoreTvEvent) { 30 | yield* _fetchMoreTvState(event.type, event.id, event.currentPageIndex); 31 | } 32 | 33 | if (event is LoadTvDetailsEvent) { 34 | yield* _processTvDetailsState(event.id); 35 | } 36 | 37 | if (event is LoadTvSeasonDetailsEvent) { 38 | yield* _processSeasonDetailEvent(event.id, event.seasonNo); 39 | } 40 | } 41 | 42 | Stream _fetchTvState( 43 | TvCat type, int id, int currentPageIndex) async* { 44 | yield TvLoadingState(); 45 | 46 | var tvs = await tvRepository.getTvShows(type, id, currentPageIndex); 47 | 48 | if (tvs != null) { 49 | yield TvLoadedState(tvs: tvs); 50 | } else { 51 | yield TvErrorState(); 52 | } 53 | } 54 | 55 | Stream _fetchMoreTvState( 56 | TvCat type, int id, int currentPageIndex) async* { 57 | // yield LoadingState(); 58 | 59 | var tvs = await tvRepository.getTvShows(type, id, currentPageIndex); 60 | if (tvs != null) { 61 | yield MoreTvLoadedState(tvs: tvs); 62 | } else { 63 | yield TvErrorState(); 64 | } 65 | } 66 | 67 | Stream _processTvDetailsState(int id) async* { 68 | yield TvLoadingState(); 69 | TvDetails tvDetails = await tvRepository.getTvDetails(id); 70 | var videoDetails = await tvRepository.getTvVideos(id); 71 | var similarTvs = await tvRepository.getTvShows(TvCat.Similar, id, 1); 72 | var tvCredit = await tvRepository.getTvCredits(id); 73 | // 74 | // print(tvDetails); 75 | // print(videoDetails); 76 | // print(similarTvs); 77 | // print(tvCredit); 78 | if (tvDetails != null) 79 | yield TvDetailsReadyState( 80 | tvDetails: tvDetails, 81 | videoDetails: videoDetails, 82 | similarTvs: similarTvs, 83 | credit: tvCredit); 84 | } 85 | 86 | Stream _processSeasonDetailEvent(int tvId, int seasonNo) async* { 87 | yield TvLoadingState(); 88 | TvSeasonDetails tvSeasonDetails = 89 | await tvRepository.getTvSeasonDetails(tvId, seasonNo); 90 | if (tvSeasonDetails != null) { 91 | yield TvSeasonDetailsState(tvSeasonDetails: tvSeasonDetails); 92 | } else { 93 | yield TvErrorState(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/bloc/tv_bloc/tv_event.dart: -------------------------------------------------------------------------------- 1 | part of 'tv_bloc.dart'; 2 | 3 | abstract class TvEvent extends Equatable { 4 | const TvEvent(); 5 | } 6 | 7 | class LoadTvEvent extends TvEvent { 8 | final TvCat type; 9 | final int id; 10 | final currentPageIndex; 11 | 12 | LoadTvEvent({this.type, this.id, this.currentPageIndex}); 13 | 14 | @override 15 | List get props => [type,id,currentPageIndex]; 16 | } 17 | 18 | class LoadMoreTvEvent extends TvEvent{ 19 | final TvCat type; 20 | final int id; 21 | final currentPageIndex; 22 | 23 | LoadMoreTvEvent({this.type, this.id, this.currentPageIndex}); 24 | 25 | @override 26 | List get props => [type,id,currentPageIndex]; 27 | 28 | } 29 | 30 | class LoadTvDetailsEvent extends TvEvent { 31 | final int id; 32 | LoadTvDetailsEvent({ 33 | this.id, 34 | }); 35 | 36 | @override 37 | List get props => [id]; 38 | } 39 | class LoadTvSeasonDetailsEvent extends TvEvent { 40 | final int id; 41 | final int seasonNo; 42 | LoadTvSeasonDetailsEvent({ 43 | this.id, 44 | this.seasonNo 45 | }); 46 | 47 | @override 48 | List get props => [id,seasonNo]; 49 | } 50 | -------------------------------------------------------------------------------- /lib/bloc/tv_bloc/tv_state.dart: -------------------------------------------------------------------------------- 1 | part of 'tv_bloc.dart'; 2 | 3 | abstract class TvState extends Equatable { 4 | const TvState(); 5 | } 6 | 7 | class TvInitial extends TvState { 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class TvLoadingState extends TvState { 13 | @override 14 | List get props => []; 15 | } 16 | 17 | class LoadingState extends TvState { 18 | @override 19 | List get props => []; 20 | } 21 | 22 | class TvLoadedState extends TvState { 23 | final Tv tvs; 24 | 25 | TvLoadedState({ 26 | this.tvs, 27 | }); 28 | 29 | @override 30 | List get props => [tvs]; 31 | } 32 | 33 | class MoreTvLoadedState extends TvState { 34 | final Tv tvs; 35 | 36 | MoreTvLoadedState({ 37 | this.tvs, 38 | }); 39 | 40 | @override 41 | List get props => [tvs]; 42 | } 43 | 44 | class TvErrorState extends TvState{ 45 | @override 46 | List get props => []; 47 | 48 | } 49 | 50 | class TvDetailsReadyState extends TvState { 51 | final TvDetails tvDetails; 52 | final VideoDetails videoDetails; 53 | final Tv similarTvs; 54 | final Credit credit; 55 | 56 | TvDetailsReadyState({ 57 | this.tvDetails, 58 | this.videoDetails, 59 | this.similarTvs, 60 | this.credit, 61 | }); 62 | 63 | @override 64 | List get props => [tvDetails, videoDetails, similarTvs,credit]; 65 | } 66 | 67 | class TvSeasonDetailsState extends TvState { 68 | final TvSeasonDetails tvSeasonDetails; 69 | TvSeasonDetailsState({ 70 | this.tvSeasonDetails 71 | }); 72 | 73 | @override 74 | List get props => [tvSeasonDetails]; 75 | } -------------------------------------------------------------------------------- /lib/fragments/discover_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/model/movie_list.dart'; 2 | import 'package:MovieDB/pages/loading_text_widget.dart'; 3 | import 'package:MovieDB/repository/constants.dart'; 4 | import 'package:firebase_auth/firebase_auth.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_bloc/flutter_bloc.dart'; 7 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 8 | import 'package:MovieDB/bloc/movies_bloc/bloc.dart'; 9 | import 'package:MovieDB/repository/movie_repository.dart'; 10 | import 'package:MovieDB/widgets/movie_item_horizontal.dart'; 11 | import 'package:MovieDB/widgets/movie_item_vertical.dart'; 12 | 13 | class DiscoverListFragment extends StatefulWidget { 14 | final int id; 15 | final String sortQuery; 16 | final List genres; 17 | final String year; 18 | final MediaType mediaType; 19 | final FirebaseUser user; 20 | 21 | DiscoverListFragment({ 22 | Key key, 23 | this.id, 24 | this.mediaType, 25 | this.sortQuery, 26 | this.genres, 27 | this.year, 28 | this.user 29 | }) : super(key: key); 30 | 31 | @override 32 | _DiscoverListFragmentState createState() => _DiscoverListFragmentState(); 33 | } 34 | 35 | class _DiscoverListFragmentState extends State { 36 | bool isVertical = false; 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | // if(widget.user!=null){ 41 | // // BlocProvider.of(context).add(LoadWatchListMoviesEvent(uid: widget.user.uid)); 42 | // } 43 | return Container( 44 | height: MediaQuery.of(context).size.height, 45 | child: FutureBuilder( 46 | future: new MovieRepository().discover(widget.sortQuery, widget.genres, widget.year, widget.mediaType,1), 47 | builder: (BuildContext context, AsyncSnapshot snapshot) { 48 | if (snapshot.data == null) { 49 | return Center( 50 | child: LoadingTextWidget(baseColor: Colors.red,highlightColor: Colors.yellow,text: "Loading...",), 51 | ); 52 | } 53 | MovieList movieList=snapshot.data; 54 | print('Snapshot:${movieList.results.length}'); 55 | return DiscoverListLayout( 56 | mediaType: widget.mediaType, 57 | user: widget.user, 58 | item: snapshot.data, 59 | sortQuery: widget.sortQuery, 60 | genres: widget.genres, 61 | isVertical: isVertical, 62 | ); 63 | }, 64 | ), 65 | ); 66 | } 67 | } 68 | 69 | class DiscoverListLayout extends StatefulWidget { 70 | final dynamic item; 71 | final MediaType mediaType; 72 | final String sortQuery; 73 | final List genres; 74 | final String year; 75 | final bool isVertical; 76 | final FirebaseUser user; 77 | 78 | DiscoverListLayout( 79 | {Key key, this.item, this.mediaType,this.isVertical,this.genres,this.sortQuery,this.year, this.user}) 80 | : super(key: key); 81 | 82 | @override 83 | _DiscoverListLayoutState createState() => _DiscoverListLayoutState(); 84 | } 85 | 86 | class _DiscoverListLayoutState extends State { 87 | ScrollController scrollController = ScrollController(); 88 | 89 | var movies=[]; 90 | 91 | int currentPage = 1; 92 | int totalPage; 93 | bool isLoading = false; 94 | 95 | void loadMoreMovies() async { 96 | var mMovies = await new MovieRepository() 97 | .discover(widget.sortQuery,widget.genres,widget.year,widget.mediaType, currentPage + 1); 98 | if (mMovies != null) { 99 | currentPage = mMovies.page; 100 | // print('$currentPage/$totalPage'); 101 | mMovies.results.add(null); 102 | if (mounted) 103 | setState(() { 104 | isLoading = false; 105 | movies.addAll(mMovies.results); 106 | }); 107 | } 108 | } 109 | 110 | bool onNotificatin(ScrollNotification notification) { 111 | if (notification is ScrollUpdateNotification) { 112 | if (scrollController.position.maxScrollExtent == 113 | scrollController.position.pixels) { 114 | // print("Ended:"); 115 | // _appBloc.add(ListScrollEvent(load: true)); 116 | if (!isLoading) { 117 | if (currentPage < totalPage) loadMoreMovies(); 118 | isLoading = true; 119 | } 120 | } 121 | } 122 | return true; 123 | } 124 | 125 | @override 126 | void initState() { 127 | movies = widget.item.results; 128 | totalPage = widget.item.totalPages; 129 | super.initState(); 130 | } 131 | 132 | @override 133 | void dispose() { 134 | super.dispose(); 135 | } 136 | 137 | @override 138 | Widget build(BuildContext context) { 139 | return widget.isVertical 140 | ? NotificationListener( 141 | onNotification: onNotificatin, 142 | child: ListView.builder( 143 | controller: scrollController, 144 | physics: BouncingScrollPhysics(), 145 | itemCount: movies.length, 146 | // shrinkWrap: true, 147 | itemBuilder: (BuildContext context, int index) { 148 | // print(movies[index].title); 149 | // Movie movie=movies[index]; 150 | if (index == movies.length - 1) { 151 | movies.removeLast(); 152 | return Center( 153 | child: SpinKitThreeBounce( 154 | color: Colors.greenAccent, 155 | )); 156 | } 157 | // if(movies[index]==null) 158 | // return SizedBox.shrink(); 159 | if (movies[index] != null) { 160 | // print(movies[index].title); 161 | return MovieItemVertical( 162 | movie: movies[index], 163 | user: widget.user, 164 | ); 165 | } 166 | return SizedBox.shrink(); 167 | }, 168 | ), 169 | ) 170 | : NotificationListener( 171 | onNotification: onNotificatin, 172 | child: GridView.builder( 173 | controller: scrollController, 174 | itemCount: movies.length, 175 | physics: BouncingScrollPhysics(), 176 | itemBuilder: (BuildContext context, int index) { 177 | if (index == movies.length - 1) { 178 | movies.removeLast(); 179 | return Center( 180 | child: CircularProgressIndicator(), 181 | ); 182 | } 183 | return MovieItemHorizontal( 184 | movie: movies[index], 185 | user: widget.user, 186 | ); 187 | }, 188 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 189 | childAspectRatio: 0.5, crossAxisCount: 3), 190 | ), 191 | ); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /lib/fragments/movies_category.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | 5 | import 'package:MovieDB/bloc/app_bloc/app_bloc.dart'; 6 | import 'package:MovieDB/bloc/auth_bloc/auth_bloc.dart'; 7 | import 'package:MovieDB/bloc/auth_bloc/auth_state.dart'; 8 | import 'package:MovieDB/repository/movie_repository.dart'; 9 | import 'package:MovieDB/widgets/movie_list_row.dart'; 10 | 11 | class MainPage extends StatelessWidget { 12 | const MainPage({ 13 | Key key, 14 | }) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return SingleChildScrollView( 19 | physics: BouncingScrollPhysics(), 20 | child: BlocBuilder( 21 | bloc: BlocProvider.of(context), 22 | builder: (BuildContext context, AuthState state) { 23 | if (state is AuthLoginState) { 24 | User user=state.user; 25 | return Column( 26 | crossAxisAlignment: CrossAxisAlignment.start, 27 | mainAxisAlignment: MainAxisAlignment.start, 28 | children: [ 29 | MovieListRow( 30 | title: "Now Playing", 31 | type: MovieCat.NowPlaying, 32 | user: user, 33 | ), 34 | MovieListRow( 35 | title: "Top Rated", 36 | type: MovieCat.TopRated, 37 | user: user, 38 | ), 39 | MovieListRow( 40 | title: "Popular", 41 | type: MovieCat.Popular, 42 | user: user, 43 | ), 44 | MovieListRow( 45 | title: "Upcoming", 46 | type: MovieCat.Upcoming, 47 | user: user, 48 | ), 49 | ], 50 | ); 51 | } 52 | return SizedBox.shrink(); 53 | }, 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | class ModalContent extends StatefulWidget { 60 | ModalContent({ 61 | Key key, 62 | }) : super(key: key); 63 | 64 | @override 65 | _ModalContentState createState() => _ModalContentState(); 66 | } 67 | 68 | class _ModalContentState extends State { 69 | bool value = false; 70 | 71 | @override 72 | Widget build(BuildContext context) { 73 | // final _appBloc = BlocProvider.of(context); 74 | // _appBloc.add(GetThemeValueEvent()); 75 | 76 | return Padding( 77 | padding: const EdgeInsets.symmetric(vertical: 16), 78 | child: Container( 79 | height: 200, 80 | child: Column( 81 | crossAxisAlignment: CrossAxisAlignment.start, 82 | children: [ 83 | Padding( 84 | padding: const EdgeInsets.all(16.0), 85 | child: Text( 86 | "App Settings", 87 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), 88 | ), 89 | ), 90 | BlocBuilder( 91 | // bloc: _appBloc, 92 | builder: (BuildContext context, AppState state) { 93 | BlocProvider.of(context).add(GetThemeValueEvent()); 94 | 95 | if (state is ThemeChangedState) { 96 | value = state.value; 97 | } 98 | 99 | if (state is GetThemeValueState) { 100 | value = state.value; 101 | } 102 | 103 | return SwitchListTile( 104 | title: Text("Dark Theme"), 105 | value: value, 106 | onChanged: (val) => BlocProvider.of(context) 107 | .add(ChangeThemeEvent(value: val)), 108 | ); 109 | }, 110 | ), 111 | ], 112 | ), 113 | ), 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/fragments/news_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | 4 | class NewsPage extends StatefulWidget { 5 | @override 6 | _NewsPageState createState() => _NewsPageState(); 7 | } 8 | 9 | class _NewsPageState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Center( 13 | child: Text("News Section coming soon"), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/fragments/tv_category.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/repository/tv_series_repository.dart'; 2 | import 'package:MovieDB/widgets/tv_list_row.dart'; 3 | import 'package:firebase_auth/firebase_auth.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | 7 | import 'package:MovieDB/bloc/auth_bloc/auth_bloc.dart'; 8 | import 'package:MovieDB/bloc/auth_bloc/auth_state.dart'; 9 | import 'package:MovieDB/repository/movie_repository.dart'; 10 | import 'package:MovieDB/widgets/movie_list_row.dart'; 11 | 12 | class TvCategory extends StatelessWidget { 13 | const TvCategory({ 14 | Key key, 15 | }) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return SingleChildScrollView( 20 | physics: BouncingScrollPhysics(), 21 | child: BlocBuilder( 22 | bloc: BlocProvider.of(context), 23 | builder: (BuildContext context, AuthState state) { 24 | if (state is AuthLoginState) { 25 | User user = state.user; 26 | return Column( 27 | crossAxisAlignment: CrossAxisAlignment.start, 28 | mainAxisAlignment: MainAxisAlignment.start, 29 | children: [ 30 | TvListRow( 31 | title: "On The Air", 32 | type: TvCat.OnTheAir, 33 | user: user, 34 | ), 35 | TvListRow( 36 | title: "Airing Today", 37 | type: TvCat.AiringToday, 38 | user: user, 39 | ), 40 | // TvListRow( 41 | // title: "Latest", 42 | // type: TvCat.Latest, 43 | // user: user, 44 | // ), 45 | TvListRow( 46 | title: "Popular", 47 | type: TvCat.Popular, 48 | user: user, 49 | ), 50 | TvListRow( 51 | title: "Top Rated", 52 | type: TvCat.TopRated, 53 | user: user, 54 | ), 55 | ], 56 | ); 57 | } 58 | return SizedBox.shrink(); 59 | }, 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/bloc/tv_bloc/tv_bloc.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:firebase_core/firebase_core.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_bloc/flutter_bloc.dart'; 7 | import 'package:MovieDB/bloc/app_bloc/app_bloc.dart'; 8 | import 'package:MovieDB/bloc/auth_bloc/bloc.dart'; 9 | import 'package:MovieDB/bloc/movies_bloc/bloc.dart'; 10 | import 'package:MovieDB/pages/home.dart'; 11 | // import 'package:google_fonts/google_fonts.dart'; 12 | 13 | void main() async { 14 | WidgetsFlutterBinding.ensureInitialized(); 15 | EquatableConfig.stringify = kDebugMode; 16 | // Bloc.observer = SimpleBlocObserver(); 17 | await Firebase.initializeApp(); 18 | 19 | runApp(MultiBlocProvider( 20 | providers: [ 21 | BlocProvider( 22 | create: (BuildContext context) => AppBloc(), 23 | ), 24 | BlocProvider( 25 | create: (BuildContext context) => MoviesBloc(), 26 | ), 27 | BlocProvider( 28 | create: (BuildContext context) => TvBloc(), 29 | ), 30 | BlocProvider( 31 | create: (BuildContext context) => AuthBloc(), 32 | ) 33 | ], 34 | child: MyApp(), 35 | ) 36 | // BlocProvider( 37 | // child: MyApp(), 38 | // create: (context) => AppBloc(), 39 | // ) 40 | ); 41 | } 42 | 43 | class MyApp extends StatelessWidget { 44 | @override 45 | Widget build(BuildContext context) { 46 | BlocProvider.of(context).add(GetThemeValueEvent()); 47 | BlocProvider.of(context).add(ListenToLoginEvent()); 48 | return BlocBuilder( 49 | builder: (BuildContext context, AppState state) { 50 | // BlocProvider.of(context).add(GetThemeValueEvent()); 51 | 52 | bool value = false; 53 | 54 | if (state is GetThemeValueState) { 55 | value = state.value; 56 | } 57 | if (state is ThemeChangedState) { 58 | value = state.value; 59 | } 60 | 61 | return MaterialApp( 62 | title: 'Flutter Demo', 63 | debugShowCheckedModeBanner: false, 64 | theme: value 65 | ? ThemeData( 66 | brightness: Brightness.dark, 67 | fontFamily: "Poppins-Regular", 68 | ) 69 | : ThemeData( 70 | brightness: Brightness.light, 71 | fontFamily: "Poppins-Regular") 72 | .copyWith( 73 | primaryColor: Colors.orange, accentColor: Colors.orange), 74 | home: Home(), 75 | ); 76 | }, 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/model/credit.dart: -------------------------------------------------------------------------------- 1 | 2 | class Credit { 3 | int id; 4 | List cast; 5 | List crew; 6 | 7 | Credit({this.id, this.cast, this.crew}); 8 | 9 | Credit.fromJson(Map json) { 10 | id = json['id']; 11 | if (json['cast'] != null) { 12 | cast = new List(); 13 | json['cast'].forEach((v) { 14 | cast.add(new Cast.fromJson(v)); 15 | }); 16 | } 17 | if (json['crew'] != null) { 18 | crew = new List(); 19 | json['crew'].forEach((v) { 20 | crew.add(new Crew.fromJson(v)); 21 | }); 22 | } 23 | } 24 | 25 | Map toJson() { 26 | final Map data = new Map(); 27 | data['id'] = this.id; 28 | if (this.cast != null) { 29 | data['cast'] = this.cast.map((v) => v.toJson()).toList(); 30 | } 31 | if (this.crew != null) { 32 | data['crew'] = this.crew.map((v) => v.toJson()).toList(); 33 | } 34 | return data; 35 | } 36 | } 37 | 38 | class Cast { 39 | int castId; 40 | String character; 41 | String creditId; 42 | int gender; 43 | int id; 44 | String name; 45 | int order; 46 | String profilePath; 47 | 48 | Cast( 49 | {this.castId, 50 | this.character, 51 | this.creditId, 52 | this.gender, 53 | this.id, 54 | this.name, 55 | this.order, 56 | this.profilePath}); 57 | 58 | Cast.fromJson(Map json) { 59 | castId = json['cast_id']; 60 | character = json['character']; 61 | creditId = json['credit_id']; 62 | gender = json['gender']; 63 | id = json['id']; 64 | name = json['name']; 65 | order = json['order']; 66 | profilePath = json['profile_path']; 67 | } 68 | 69 | Map toJson() { 70 | final Map data = new Map(); 71 | data['cast_id'] = this.castId; 72 | data['character'] = this.character; 73 | data['credit_id'] = this.creditId; 74 | data['gender'] = this.gender; 75 | data['id'] = this.id; 76 | data['name'] = this.name; 77 | data['order'] = this.order; 78 | data['profile_path'] = this.profilePath; 79 | return data; 80 | } 81 | } 82 | 83 | class Crew { 84 | String creditId; 85 | String department; 86 | int gender; 87 | int id; 88 | String job; 89 | String name; 90 | String profilePath; 91 | 92 | Crew( 93 | {this.creditId, 94 | this.department, 95 | this.gender, 96 | this.id, 97 | this.job, 98 | this.name, 99 | this.profilePath}); 100 | 101 | Crew.fromJson(Map json) { 102 | creditId = json['credit_id']; 103 | department = json['department']; 104 | gender = json['gender']; 105 | id = json['id']; 106 | job = json['job']; 107 | name = json['name']; 108 | profilePath = json['profile_path']; 109 | } 110 | 111 | Map toJson() { 112 | final Map data = new Map(); 113 | data['credit_id'] = this.creditId; 114 | data['department'] = this.department; 115 | data['gender'] = this.gender; 116 | data['id'] = this.id; 117 | data['job'] = this.job; 118 | data['name'] = this.name; 119 | data['profile_path'] = this.profilePath; 120 | return data; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/model/favourite.dart: -------------------------------------------------------------------------------- 1 | class Favourite { 2 | final int id; 3 | final String uid; 4 | 5 | Favourite({ 6 | this.id, 7 | this.uid, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/model/genre.dart: -------------------------------------------------------------------------------- 1 | // To parse this JSON data, do 2 | // 3 | // final genry = genryFromJson(jsonString); 4 | 5 | import 'dart:convert'; 6 | 7 | Genry genryFromJson(String str) => Genry.fromMap(json.decode(str)); 8 | 9 | String genryToJson(Genry data) => json.encode(data.toMap()); 10 | 11 | class Genry { 12 | List genres; 13 | 14 | Genry({ 15 | this.genres, 16 | }); 17 | 18 | factory Genry.fromMap(Map json) => Genry( 19 | genres: List.from(json["genres"].map((x) => Genre.fromMap(x))), 20 | ); 21 | 22 | Map toMap() => { 23 | "genres": List.from(genres.map((x) => x.toMap())), 24 | }; 25 | } 26 | 27 | class Genre { 28 | int id; 29 | String name; 30 | 31 | Genre({ 32 | this.id, 33 | this.name, 34 | }); 35 | 36 | factory Genre.fromMap(Map json) => Genre( 37 | id: json["id"], 38 | name: json["name"], 39 | ); 40 | 41 | Map toMap() => { 42 | "id": id, 43 | "name": name, 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /lib/model/movie.dart: -------------------------------------------------------------------------------- 1 | class Movie { 2 | final int id; 3 | final String title; 4 | final String overview; 5 | final String posterPath; 6 | final String backdrop; 7 | final String releaseDate; 8 | final dynamic votes; 9 | final List genries; 10 | final int page; 11 | 12 | Movie( 13 | { 14 | this.id, 15 | this.title, 16 | this.overview, 17 | this.posterPath, 18 | this.backdrop, 19 | this.releaseDate, 20 | this.votes, 21 | this.genries, 22 | this.page 23 | }); 24 | 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /lib/model/movie_images.dart: -------------------------------------------------------------------------------- 1 | // To parse this JSON data, do 2 | // 3 | // final movieImages = movieImagesFromJson(jsonString); 4 | 5 | import 'dart:convert'; 6 | 7 | MovieImages movieImagesFromJson(String str) => MovieImages.fromMap(json.decode(str)); 8 | 9 | String movieImagesToJson(MovieImages data) => json.encode(data.toMap()); 10 | 11 | class MovieImages { 12 | int id; 13 | List backdrops; 14 | List posters; 15 | 16 | MovieImages({ 17 | this.id, 18 | this.backdrops, 19 | this.posters, 20 | }); 21 | 22 | factory MovieImages.fromMap(Map json) => MovieImages( 23 | id: json["id"], 24 | backdrops: List.from(json["backdrops"].map((x) => Backdrop.fromMap(x))), 25 | posters: List.from(json["posters"].map((x) => Backdrop.fromMap(x))), 26 | ); 27 | 28 | Map toMap() => { 29 | "id": id, 30 | "backdrops": List.from(backdrops.map((x) => x.toMap())), 31 | "posters": List.from(posters.map((x) => x.toMap())), 32 | }; 33 | } 34 | 35 | class Backdrop { 36 | double aspectRatio; 37 | String filePath; 38 | int height; 39 | String iso6391; 40 | double voteAverage; 41 | int voteCount; 42 | int width; 43 | 44 | Backdrop({ 45 | this.aspectRatio, 46 | this.filePath, 47 | this.height, 48 | this.iso6391, 49 | this.voteAverage, 50 | this.voteCount, 51 | this.width, 52 | }); 53 | 54 | factory Backdrop.fromMap(Map json) => Backdrop( 55 | aspectRatio: json["aspect_ratio"].toDouble(), 56 | filePath: json["file_path"], 57 | height: json["height"], 58 | iso6391: json["iso_639_1"] == null ? null : json["iso_639_1"], 59 | voteAverage: json["vote_average"].toDouble(), 60 | voteCount: json["vote_count"], 61 | width: json["width"], 62 | ); 63 | 64 | Map toMap() => { 65 | "aspect_ratio": aspectRatio, 66 | "file_path": filePath, 67 | "height": height, 68 | "iso_639_1": iso6391 == null ? null : iso6391, 69 | "vote_average": voteAverage, 70 | "vote_count": voteCount, 71 | "width": width, 72 | }; 73 | } 74 | 75 | class Poster { 76 | double aspectRatio; 77 | String filePath; 78 | int height; 79 | String iso6391; 80 | double voteAverage; 81 | int voteCount; 82 | int width; 83 | 84 | Poster({ 85 | this.aspectRatio, 86 | this.filePath, 87 | this.height, 88 | this.iso6391, 89 | this.voteAverage, 90 | this.voteCount, 91 | this.width, 92 | }); 93 | 94 | factory Poster.fromMap(Map json) => Poster( 95 | aspectRatio: json["aspect_ratio"].toDouble(), 96 | filePath: json["file_path"], 97 | height: json["height"], 98 | iso6391: json["iso_639_1"] == null ? null : json["iso_639_1"], 99 | voteAverage: json["vote_average"].toDouble(), 100 | voteCount: json["vote_count"], 101 | width: json["width"], 102 | ); 103 | 104 | Map toMap() => { 105 | "aspect_ratio": aspectRatio, 106 | "file_path": filePath, 107 | "height": height, 108 | "iso_639_1": iso6391 == null ? null : iso6391, 109 | "vote_average": voteAverage, 110 | "vote_count": voteCount, 111 | "width": width, 112 | }; 113 | } 114 | -------------------------------------------------------------------------------- /lib/model/movie_list.dart: -------------------------------------------------------------------------------- 1 | class MovieList { 2 | List results; 3 | dynamic page; 4 | dynamic totalResults; 5 | Dates dates; 6 | dynamic totalPages; 7 | 8 | MovieList(MovieList data, 9 | {this.results, 10 | this.page, 11 | this.totalResults, 12 | this.dates, 13 | this.totalPages}); 14 | 15 | MovieList.fromJson(Map json) { 16 | if (json['results'] != null) { 17 | results = new List(); 18 | json['results'].forEach((v) { 19 | results.add(new Results.fromJson(v)); 20 | }); 21 | } 22 | page = json['page']; 23 | totalResults = json['total_results']; 24 | dates = json['dates'] != null ? new Dates.fromJson(json['dates']) : null; 25 | totalPages = json['total_pages']; 26 | } 27 | 28 | Map toJson() { 29 | final Map data = new Map(); 30 | if (this.results != null) { 31 | data['results'] = this.results.map((v) => v.toJson()).toList(); 32 | } 33 | data['page'] = this.page; 34 | data['total_results'] = this.totalResults; 35 | if (this.dates != null) { 36 | data['dates'] = this.dates.toJson(); 37 | } 38 | data['total_pages'] = this.totalPages; 39 | return data; 40 | } 41 | } 42 | 43 | class Results { 44 | dynamic popularity; 45 | dynamic voteCount; 46 | bool video; 47 | String posterPath; 48 | int id; 49 | bool adult; 50 | String backdropPath; 51 | String originalLanguage; 52 | String originalTitle; 53 | List genreIds; 54 | String title; 55 | dynamic voteAverage; 56 | String overview; 57 | String releaseDate; 58 | 59 | Results( 60 | {this.popularity, 61 | this.voteCount, 62 | this.video, 63 | this.posterPath, 64 | this.id, 65 | this.adult, 66 | this.backdropPath, 67 | this.originalLanguage, 68 | this.originalTitle, 69 | this.genreIds, 70 | this.title, 71 | this.voteAverage, 72 | this.overview, 73 | this.releaseDate}); 74 | 75 | Results.fromJson(Map json) { 76 | popularity = json['popularity']; 77 | voteCount = json['vote_count']; 78 | video = json['video']; 79 | posterPath = json['poster_path']; 80 | id = json['id']; 81 | adult = json['adult']; 82 | backdropPath = json['backdrop_path']; 83 | originalLanguage = json['original_language']; 84 | originalTitle = json['original_title']; 85 | genreIds = json['genre_ids'].cast(); 86 | title = json['title']; 87 | voteAverage = json['vote_average']; 88 | overview = json['overview']; 89 | releaseDate = json['release_date']; 90 | } 91 | 92 | Map toJson() { 93 | final Map data = new Map(); 94 | data['popularity'] = this.popularity; 95 | data['vote_count'] = this.voteCount; 96 | data['video'] = this.video; 97 | data['poster_path'] = this.posterPath; 98 | data['id'] = this.id; 99 | data['adult'] = this.adult; 100 | data['backdrop_path'] = this.backdropPath; 101 | data['original_language'] = this.originalLanguage; 102 | data['original_title'] = this.originalTitle; 103 | data['genre_ids'] = this.genreIds; 104 | data['title'] = this.title; 105 | data['vote_average'] = this.voteAverage; 106 | data['overview'] = this.overview; 107 | data['release_date'] = this.releaseDate; 108 | return data; 109 | } 110 | } 111 | 112 | class Dates { 113 | String maximum; 114 | String minimum; 115 | 116 | Dates({this.maximum, this.minimum}); 117 | 118 | Dates.fromJson(Map json) { 119 | maximum = json['maximum']; 120 | minimum = json['minimum']; 121 | } 122 | 123 | Map toJson() { 124 | final Map data = new Map(); 125 | data['maximum'] = this.maximum; 126 | data['minimum'] = this.minimum; 127 | return data; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/model/movie_review.dart: -------------------------------------------------------------------------------- 1 | // To parse this JSON data, do 2 | // 3 | // final movieReview = movieReviewFromJson(jsonString); 4 | 5 | import 'dart:convert'; 6 | 7 | MovieReview movieReviewFromJson(String str) => MovieReview.fromMap(json.decode(str)); 8 | 9 | String movieReviewToJson(MovieReview data) => json.encode(data.toMap()); 10 | 11 | class MovieReview { 12 | int id; 13 | int page; 14 | List results; 15 | dynamic totalPages; 16 | dynamic totalResults; 17 | 18 | MovieReview({ 19 | this.id, 20 | this.page, 21 | this.results, 22 | this.totalPages, 23 | this.totalResults, 24 | }); 25 | 26 | factory MovieReview.fromMap(Map json) => MovieReview( 27 | id: json["id"], 28 | page: json["page"], 29 | results: List.from(json["results"].map((x) => Result.fromMap(x))), 30 | totalPages: json["total_pages"], 31 | totalResults: json["total_results"], 32 | ); 33 | 34 | Map toMap() => { 35 | "id": id, 36 | "page": page, 37 | "results": List.from(results.map((x) => x.toMap())), 38 | "total_pages": totalPages, 39 | "total_results": totalResults, 40 | }; 41 | } 42 | 43 | class Result { 44 | String author; 45 | String content; 46 | String id; 47 | String url; 48 | 49 | Result({ 50 | this.author, 51 | this.content, 52 | this.id, 53 | this.url, 54 | }); 55 | 56 | factory Result.fromMap(Map json) => Result( 57 | author: json["author"], 58 | content: json["content"], 59 | id: json["id"], 60 | url: json["url"], 61 | ); 62 | 63 | Map toMap() => { 64 | "author": author, 65 | "content": content, 66 | "id": id, 67 | "url": url, 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /lib/model/person.dart: -------------------------------------------------------------------------------- 1 | // To parse this JSON data, do 2 | // 3 | // final person = personFromJson(jsonString); 4 | 5 | import 'dart:convert'; 6 | 7 | Person personFromJson(String str) => Person.fromMap(json.decode(str)); 8 | 9 | String personToJson(Person data) => json.encode(data.toMap()); 10 | 11 | class Person { 12 | final DateTime birthday; 13 | final String knownForDepartment; 14 | final dynamic deathday; 15 | final int id; 16 | final String name; 17 | final List alsoKnownAs; 18 | final int gender; 19 | final String biography; 20 | final double popularity; 21 | final String placeOfBirth; 22 | final String profilePath; 23 | final bool adult; 24 | final String imdbId; 25 | final String homepage; 26 | 27 | Person({ 28 | this.birthday, 29 | this.knownForDepartment, 30 | this.deathday, 31 | this.id, 32 | this.name, 33 | this.alsoKnownAs, 34 | this.gender, 35 | this.biography, 36 | this.popularity, 37 | this.placeOfBirth, 38 | this.profilePath, 39 | this.adult, 40 | this.imdbId, 41 | this.homepage, 42 | }); 43 | 44 | factory Person.fromMap(Map json) => Person( 45 | birthday: json["birthday"] == null ? null : DateTime.parse(json["birthday"]), 46 | knownForDepartment: json["known_for_department"] == null ? null : json["known_for_department"], 47 | deathday: json["deathday"], 48 | id: json["id"] == null ? null : json["id"], 49 | name: json["name"] == null ? null : json["name"], 50 | alsoKnownAs: json["also_known_as"] == null ? null : List.from(json["also_known_as"].map((x) => x)), 51 | gender: json["gender"] == null ? null : json["gender"], 52 | biography: json["biography"] == null ? null : json["biography"], 53 | popularity: json["popularity"] == null ? null : json["popularity"].toDouble(), 54 | placeOfBirth: json["place_of_birth"] == null ? null : json["place_of_birth"], 55 | profilePath: json["profile_path"] == null ? null : json["profile_path"], 56 | adult: json["adult"] == null ? null : json["adult"], 57 | imdbId: json["imdb_id"] == null ? null : json["imdb_id"], 58 | homepage: json["homepage"] == null ? null : json["homepage"], 59 | ); 60 | 61 | Map toMap() => { 62 | "birthday": birthday == null ? null : "${birthday.year.toString().padLeft(4, '0')}-${birthday.month.toString().padLeft(2, '0')}-${birthday.day.toString().padLeft(2, '0')}", 63 | "known_for_department": knownForDepartment == null ? null : knownForDepartment, 64 | "deathday": deathday, 65 | "id": id == null ? null : id, 66 | "name": name == null ? null : name, 67 | "also_known_as": alsoKnownAs == null ? null : List.from(alsoKnownAs.map((x) => x)), 68 | "gender": gender == null ? null : gender, 69 | "biography": biography == null ? null : biography, 70 | "popularity": popularity == null ? null : popularity, 71 | "place_of_birth": placeOfBirth == null ? null : placeOfBirth, 72 | "profile_path": profilePath == null ? null : profilePath, 73 | "adult": adult == null ? null : adult, 74 | "imdb_id": imdbId == null ? null : imdbId, 75 | "homepage": homepage == null ? null : homepage, 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /lib/model/person_images.dart: -------------------------------------------------------------------------------- 1 | class PersonImages { 2 | List profiles; 3 | int id; 4 | 5 | PersonImages({this.profiles, this.id}); 6 | 7 | PersonImages.fromJson(Map json) { 8 | if (json['profiles'] != null) { 9 | profiles = new List(); 10 | json['profiles'].forEach((v) { 11 | profiles.add(new Profiles.fromJson(v)); 12 | }); 13 | } 14 | id = json['id']; 15 | } 16 | 17 | Map toJson() { 18 | final Map data = new Map(); 19 | if (this.profiles != null) { 20 | data['profiles'] = this.profiles.map((v) => v.toJson()).toList(); 21 | } 22 | data['id'] = this.id; 23 | return data; 24 | } 25 | } 26 | 27 | class Profiles { 28 | dynamic width; 29 | dynamic height; 30 | dynamic voteCount; 31 | dynamic voteAverage; 32 | dynamic filePath; 33 | dynamic aspectRatio; 34 | 35 | Profiles( 36 | {this.width, 37 | this.height, 38 | this.voteCount, 39 | this.voteAverage, 40 | this.filePath, 41 | this.aspectRatio}); 42 | 43 | Profiles.fromJson(Map json) { 44 | width = json['width']; 45 | height = json['height']; 46 | voteCount = json['vote_count']; 47 | voteAverage = json['vote_average']; 48 | filePath = json['file_path']; 49 | aspectRatio = json['aspect_ratio']; 50 | } 51 | 52 | Map toJson() { 53 | final Map data = new Map(); 54 | data['width'] = this.width; 55 | data['height'] = this.height; 56 | data['vote_count'] = this.voteCount; 57 | data['vote_average'] = this.voteAverage; 58 | data['file_path'] = this.filePath; 59 | data['aspect_ratio'] = this.aspectRatio; 60 | return data; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/model/tv_list_model.dart: -------------------------------------------------------------------------------- 1 | // To parse this JSON data, do 2 | // 3 | // final tv = tvFromJson(jsonString); 4 | 5 | import 'dart:convert'; 6 | 7 | Tv tvFromJson(String str) => Tv.fromMap(json.decode(str)); 8 | 9 | String tvToJson(Tv data) => json.encode(data.toMap()); 10 | 11 | class Tv { 12 | int page; 13 | int totalResults; 14 | int totalPages; 15 | List results; 16 | 17 | Tv({ 18 | this.page, 19 | this.totalResults, 20 | this.totalPages, 21 | this.results, 22 | }); 23 | 24 | factory Tv.fromMap(Map json) => Tv( 25 | page: json["page"], 26 | totalResults: json["total_results"], 27 | totalPages: json["total_pages"], 28 | results: 29 | List.from(json["results"].map((x) => Result.fromMap(x))), 30 | ); 31 | 32 | Map toMap() => { 33 | "page": page, 34 | "total_results": totalResults, 35 | "total_pages": totalPages, 36 | "results": List.from(results.map((x) => x.toMap())), 37 | }; 38 | } 39 | 40 | class Result { 41 | String originalName; 42 | List genreIds; 43 | String name; 44 | dynamic popularity; 45 | List originCountry; 46 | dynamic voteCount; 47 | DateTime firstAirDate; 48 | String backdropPath; 49 | OriginalLanguage originalLanguage; 50 | int id; 51 | dynamic voteAverage; 52 | String overview; 53 | String posterPath; 54 | 55 | Result({ 56 | this.originalName, 57 | this.genreIds, 58 | this.name, 59 | this.popularity, 60 | this.originCountry, 61 | this.voteCount, 62 | this.firstAirDate, 63 | this.backdropPath, 64 | this.originalLanguage, 65 | this.id, 66 | this.voteAverage, 67 | this.overview, 68 | this.posterPath, 69 | }); 70 | 71 | factory Result.fromMap(Map json) => Result( 72 | originalName: json["original_name"], 73 | genreIds: List.from(json["genre_ids"].map((x) => x)), 74 | name: json["name"], 75 | popularity: json["popularity"].toDouble(), 76 | originCountry: List.from(json["origin_country"].map((x) => x)), 77 | voteCount: json["vote_count"], 78 | firstAirDate: json["first_air_date"] != null ? DateTime.parse(json["first_air_date"]) : null, 79 | backdropPath: json["backdrop_path"], 80 | originalLanguage: originalLanguageValues.map[json["original_language"]], 81 | id: json["id"], 82 | voteAverage: json["vote_average"].toDouble(), 83 | overview: json["overview"], 84 | posterPath: json["poster_path"], 85 | ); 86 | 87 | Map toMap() => { 88 | "original_name": originalName, 89 | "genre_ids": List.from(genreIds.map((x) => x)), 90 | "name": name, 91 | "popularity": popularity, 92 | "origin_country": List.from(originCountry.map((x) => x)), 93 | "vote_count": voteCount, 94 | "first_air_date": 95 | "${firstAirDate.year.toString().padLeft(4, '0')}-${firstAirDate.month.toString().padLeft(2, '0')}-${firstAirDate.day.toString().padLeft(2, '0')}", 96 | "backdrop_path": backdropPath, 97 | "original_language": originalLanguageValues.reverse[originalLanguage], 98 | "id": id, 99 | "vote_average": voteAverage, 100 | "overview": overview, 101 | "poster_path": posterPath, 102 | }; 103 | } 104 | 105 | enum OriginalLanguage { EN, NL, JA } 106 | 107 | final originalLanguageValues = EnumValues({ 108 | "en": OriginalLanguage.EN, 109 | "ja": OriginalLanguage.JA, 110 | "nl": OriginalLanguage.NL 111 | }); 112 | 113 | class EnumValues { 114 | Map map; 115 | Map reverseMap; 116 | 117 | EnumValues(this.map); 118 | 119 | Map get reverse { 120 | if (reverseMap == null) { 121 | reverseMap = map.map((k, v) => new MapEntry(v, k)); 122 | } 123 | return reverseMap; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /lib/model/tv_season_details.dart: -------------------------------------------------------------------------------- 1 | // To parse this JSON data, do 2 | // 3 | // final tvSeasonDetails = tvSeasonDetailsFromJson(jsonString); 4 | 5 | import 'dart:convert'; 6 | 7 | TvSeasonDetails tvSeasonDetailsFromJson(String str) => TvSeasonDetails.fromMap(json.decode(str)); 8 | 9 | String tvSeasonDetailsToJson(TvSeasonDetails data) => json.encode(data.toMap()); 10 | 11 | class TvSeasonDetails { 12 | String id; 13 | DateTime airDate; 14 | List episodes; 15 | String name; 16 | String overview; 17 | int tvSeasonDetailsId; 18 | String posterPath; 19 | int seasonNumber; 20 | 21 | TvSeasonDetails({ 22 | this.id, 23 | this.airDate, 24 | this.episodes, 25 | this.name, 26 | this.overview, 27 | this.tvSeasonDetailsId, 28 | this.posterPath, 29 | this.seasonNumber, 30 | }); 31 | 32 | factory TvSeasonDetails.fromMap(Map json) => TvSeasonDetails( 33 | id: json["_id"], 34 | airDate: DateTime.parse(json["air_date"]), 35 | episodes: List.from(json["episodes"].map((x) => Episode.fromMap(x))), 36 | name: json["name"], 37 | overview: json["overview"], 38 | tvSeasonDetailsId: json["id"], 39 | posterPath: json["poster_path"], 40 | seasonNumber: json["season_number"], 41 | ); 42 | 43 | Map toMap() => { 44 | "_id": id, 45 | "air_date": "${airDate.year.toString().padLeft(4, '0')}-${airDate.month.toString().padLeft(2, '0')}-${airDate.day.toString().padLeft(2, '0')}", 46 | "episodes": List.from(episodes.map((x) => x.toMap())), 47 | "name": name, 48 | "overview": overview, 49 | "id": tvSeasonDetailsId, 50 | "poster_path": posterPath, 51 | "season_number": seasonNumber, 52 | }; 53 | } 54 | 55 | class Episode { 56 | DateTime airDate; 57 | int episodeNumber; 58 | int id; 59 | String name; 60 | String overview; 61 | String productionCode; 62 | int seasonNumber; 63 | int showId; 64 | String stillPath; 65 | double voteAverage; 66 | int voteCount; 67 | List crew; 68 | List guestStars; 69 | 70 | Episode({ 71 | this.airDate, 72 | this.episodeNumber, 73 | this.id, 74 | this.name, 75 | this.overview, 76 | this.productionCode, 77 | this.seasonNumber, 78 | this.showId, 79 | this.stillPath, 80 | this.voteAverage, 81 | this.voteCount, 82 | this.crew, 83 | this.guestStars, 84 | }); 85 | 86 | factory Episode.fromMap(Map json) => Episode( 87 | airDate: DateTime.parse(json["air_date"]), 88 | episodeNumber: json["episode_number"], 89 | id: json["id"], 90 | name: json["name"], 91 | overview: json["overview"], 92 | productionCode: json["production_code"], 93 | seasonNumber: json["season_number"], 94 | showId: json["show_id"], 95 | stillPath: json["still_path"], 96 | voteAverage: json["vote_average"].toDouble(), 97 | voteCount: json["vote_count"], 98 | crew: List.from(json["crew"].map((x) => Crew.fromMap(x))), 99 | guestStars: List.from(json["guest_stars"].map((x) => GuestStar.fromMap(x))), 100 | ); 101 | 102 | Map toMap() => { 103 | "air_date": "${airDate.year.toString().padLeft(4, '0')}-${airDate.month.toString().padLeft(2, '0')}-${airDate.day.toString().padLeft(2, '0')}", 104 | "episode_number": episodeNumber, 105 | "id": id, 106 | "name": name, 107 | "overview": overview, 108 | "production_code": productionCode, 109 | "season_number": seasonNumber, 110 | "show_id": showId, 111 | "still_path": stillPath, 112 | "vote_average": voteAverage, 113 | "vote_count": voteCount, 114 | "crew": List.from(crew.map((x) => x.toMap())), 115 | "guest_stars": List.from(guestStars.map((x) => x.toMap())), 116 | }; 117 | } 118 | 119 | class Crew { 120 | int id; 121 | String creditId; 122 | String name; 123 | Department department; 124 | Job job; 125 | int gender; 126 | String profilePath; 127 | 128 | Crew({ 129 | this.id, 130 | this.creditId, 131 | this.name, 132 | this.department, 133 | this.job, 134 | this.gender, 135 | this.profilePath, 136 | }); 137 | 138 | factory Crew.fromMap(Map json) => Crew( 139 | id: json["id"], 140 | creditId: json["credit_id"], 141 | name: json["name"], 142 | department: departmentValues.map[json["department"]], 143 | job: jobValues.map[json["job"]], 144 | gender: json["gender"], 145 | profilePath: json["profile_path"] == null ? null : json["profile_path"], 146 | ); 147 | 148 | Map toMap() => { 149 | "id": id, 150 | "credit_id": creditId, 151 | "name": name, 152 | "department": departmentValues.reverse[department], 153 | "job": jobValues.reverse[job], 154 | "gender": gender, 155 | "profile_path": profilePath == null ? null : profilePath, 156 | }; 157 | } 158 | 159 | enum Department { WRITING, DIRECTING } 160 | 161 | final departmentValues = EnumValues({ 162 | "Directing": Department.DIRECTING, 163 | "Writing": Department.WRITING 164 | }); 165 | 166 | enum Job { WRITER, DIRECTOR } 167 | 168 | final jobValues = EnumValues({ 169 | "Director": Job.DIRECTOR, 170 | "Writer": Job.WRITER 171 | }); 172 | 173 | class GuestStar { 174 | int id; 175 | String name; 176 | String creditId; 177 | Character character; 178 | int order; 179 | int gender; 180 | String profilePath; 181 | 182 | GuestStar({ 183 | this.id, 184 | this.name, 185 | this.creditId, 186 | this.character, 187 | this.order, 188 | this.gender, 189 | this.profilePath, 190 | }); 191 | 192 | factory GuestStar.fromMap(Map json) => GuestStar( 193 | id: json["id"], 194 | name: json["name"], 195 | creditId: json["credit_id"], 196 | character: characterValues.map[json["character"]], 197 | order: json["order"], 198 | gender: json["gender"], 199 | profilePath: json["profile_path"] == null ? null : json["profile_path"], 200 | ); 201 | 202 | Map toMap() => { 203 | "id": id, 204 | "name": name, 205 | "credit_id": creditId, 206 | "character": characterValues.reverse[character], 207 | "order": order, 208 | "gender": gender, 209 | "profile_path": profilePath == null ? null : profilePath, 210 | }; 211 | } 212 | 213 | enum Character { EMPTY, ANGELA_DARE_GWEN_THE_WAITRESS_HOMER_S_COUSIN_S_WIFE_VOICE } 214 | 215 | final characterValues = EnumValues({ 216 | "Angela Dare / Gwen the waitress / Homer's cousin's wife (voice)": Character.ANGELA_DARE_GWEN_THE_WAITRESS_HOMER_S_COUSIN_S_WIFE_VOICE, 217 | "": Character.EMPTY 218 | }); 219 | 220 | class EnumValues { 221 | Map map; 222 | Map reverseMap; 223 | 224 | EnumValues(this.map); 225 | 226 | Map get reverse { 227 | if (reverseMap == null) { 228 | reverseMap = map.map((k, v) => new MapEntry(v, k)); 229 | } 230 | return reverseMap; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /lib/model/user.dart: -------------------------------------------------------------------------------- 1 | class User { 2 | String id; 3 | String firstname; 4 | String lastname; 5 | String email; 6 | String avatarUrl; 7 | User({ 8 | this.id, 9 | this.firstname, 10 | this.lastname, 11 | this.email, 12 | this.avatarUrl, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /lib/model/video_details.dart: -------------------------------------------------------------------------------- 1 | class VideoDetails { 2 | int id; 3 | List results; 4 | 5 | VideoDetails({this.id, this.results}); 6 | 7 | VideoDetails.fromJson(Map json) { 8 | id = json['id']; 9 | if (json['results'] != null) { 10 | results = new List(); 11 | json['results'].forEach((v) { 12 | results.add(new Results.fromJson(v)); 13 | }); 14 | } 15 | } 16 | 17 | Map toJson() { 18 | final Map data = new Map(); 19 | data['id'] = this.id; 20 | if (this.results != null) { 21 | data['results'] = this.results.map((v) => v.toJson()).toList(); 22 | } 23 | return data; 24 | } 25 | } 26 | 27 | class Results { 28 | String id; 29 | String iso6391; 30 | String iso31661; 31 | String key; 32 | String name; 33 | String site; 34 | dynamic size; 35 | String type; 36 | 37 | Results( 38 | {this.id, 39 | this.iso6391, 40 | this.iso31661, 41 | this.key, 42 | this.name, 43 | this.site, 44 | this.size, 45 | this.type}); 46 | 47 | Results.fromJson(Map json) { 48 | id = json['id']; 49 | iso6391 = json['iso_639_1']; 50 | iso31661 = json['iso_3166_1']; 51 | key = json['key']; 52 | name = json['name']; 53 | site = json['site']; 54 | size = json['size']; 55 | type = json['type']; 56 | } 57 | 58 | Map toJson() { 59 | final Map data = new Map(); 60 | data['id'] = this.id; 61 | data['iso_639_1'] = this.iso6391; 62 | data['iso_3166_1'] = this.iso31661; 63 | data['key'] = this.key; 64 | data['name'] = this.name; 65 | data['site'] = this.site; 66 | data['size'] = this.size; 67 | data['type'] = this.type; 68 | return data; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/pages/auth_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:MovieDB/pages/sign_in_page.dart'; 3 | 4 | import 'sign_up_page.dart'; 5 | 6 | class AuthPage extends StatefulWidget { 7 | @override 8 | _AuthPageState createState() => _AuthPageState(); 9 | } 10 | 11 | class _AuthPageState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | body: ListView( 16 | children: [ 17 | Padding( 18 | padding: const EdgeInsets.all(32.0), 19 | child: Column( 20 | crossAxisAlignment: CrossAxisAlignment.start, 21 | children: [ 22 | SizedBox( 23 | height: 150, 24 | ), 25 | Container( 26 | padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), 27 | decoration: BoxDecoration( 28 | color: Colors.blue[50], 29 | borderRadius: BorderRadius.circular(16)), 30 | child: Text("V 1.32.1", style: TextStyle(color: Colors.blue)), 31 | ), 32 | SizedBox( 33 | height: 10, 34 | ), 35 | Text("What's New?", 36 | style: TextStyle( 37 | color: Colors.blue, 38 | fontWeight: FontWeight.w600, 39 | fontSize: 32)), 40 | SizedBox( 41 | height: 40, 42 | ), 43 | ListItem( 44 | title: "Connect To Multiple Speakers", 45 | subTitle: 46 | "You can now connect to multiple bluetooth speakers at a time.", 47 | icon: Icons.bluetooth_connected, 48 | iconColor: Colors.deepPurpleAccent, 49 | ), 50 | SizedBox( 51 | height: 32, 52 | ), 53 | ListItem( 54 | title: "Broadcast Party Value", 55 | subTitle: "Notify friends nearby of an ongoing party.", 56 | icon: Icons.settings_input_antenna, 57 | iconColor: Colors.orangeAccent, 58 | ), 59 | SizedBox( 60 | height: 32, 61 | ), 62 | ListItem( 63 | title: "Flutter Interactive", 64 | subTitle: 65 | "Run a codelab from your mobile phone and with the world.", 66 | icon: Icons.insert_emoticon, 67 | iconColor: Colors.green, 68 | ), 69 | SizedBox( 70 | height: 50, 71 | ), 72 | InkWell( 73 | onTap: () { 74 | Navigator.of(context).push( 75 | MaterialPageRoute(builder: (BuildContext context)=>SignInPage())); 76 | }, 77 | child: Container( 78 | padding: EdgeInsets.symmetric(vertical: 16), 79 | decoration: BoxDecoration(color: Colors.blue), 80 | child: Center( 81 | child: Text( 82 | "Sign In", 83 | style: TextStyle( 84 | color: Colors.white, fontWeight: FontWeight.w500), 85 | ), 86 | ), 87 | ), 88 | ), 89 | SizedBox( 90 | height: 16, 91 | ), 92 | InkWell( 93 | onTap: () { 94 | Navigator.of(context).push( 95 | MaterialPageRoute(builder: (BuildContext context)=>SignUpPage())); 96 | }, 97 | child: Container( 98 | padding: EdgeInsets.symmetric(vertical: 16), 99 | decoration: BoxDecoration( 100 | border: Border.fromBorderSide( 101 | BorderSide(color: Colors.blue, width: 1))), 102 | child: Center( 103 | child: Text( 104 | "Sign Up", 105 | style: TextStyle( 106 | color: Colors.blue, fontWeight: FontWeight.w900), 107 | ), 108 | ), 109 | ), 110 | ) 111 | ], 112 | ), 113 | ), 114 | ], 115 | )); 116 | } 117 | } 118 | 119 | class ListItem extends StatelessWidget { 120 | final String title; 121 | final String subTitle; 122 | final Color iconColor; 123 | final IconData icon; 124 | ListItem({ 125 | Key key, 126 | this.title, 127 | this.subTitle, 128 | this.iconColor, 129 | this.icon, 130 | }) : super(key: key); 131 | 132 | @override 133 | Widget build(BuildContext context) { 134 | return Row( 135 | crossAxisAlignment: CrossAxisAlignment.start, 136 | children: [ 137 | Expanded( 138 | flex: 1, 139 | child: Column( 140 | crossAxisAlignment: CrossAxisAlignment.start, 141 | mainAxisSize: MainAxisSize.min, 142 | children: [ 143 | Icon(icon, color: iconColor), 144 | 145 | ], 146 | )), 147 | Expanded( 148 | flex: 8, 149 | child: Padding( 150 | padding: const EdgeInsets.symmetric(horizontal: 8.0), 151 | child: Column( 152 | crossAxisAlignment: CrossAxisAlignment.start, 153 | children: [ 154 | Text( 155 | title, 156 | style: TextStyle( 157 | color: Colors.black, 158 | fontSize: 16, 159 | fontWeight: FontWeight.bold, 160 | ), 161 | maxLines: 3, 162 | softWrap: true, 163 | ), 164 | SizedBox( 165 | height: 10, 166 | ), 167 | Text( 168 | subTitle, 169 | style: TextStyle(color: Colors.grey[600], fontSize: 12), 170 | ), 171 | ], 172 | ), 173 | ), 174 | ) 175 | ], 176 | ); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /lib/pages/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/fragments/tv_category.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_icons/flutter_icons.dart'; 6 | import 'package:MovieDB/bloc/auth_bloc/auth_bloc.dart'; 7 | import 'package:MovieDB/bloc/auth_bloc/bloc.dart'; 8 | import 'package:MovieDB/fragments/discover_page.dart'; 9 | import 'package:MovieDB/fragments/movies_category.dart'; 10 | import 'package:MovieDB/fragments/news_page.dart'; 11 | import 'package:MovieDB/pages/profile_screen.dart'; 12 | import 'package:MovieDB/widgets/auth_modal_form.dart'; 13 | 14 | import 'package:MovieDB/widgets/search_widget.dart'; 15 | 16 | class Home extends StatefulWidget { 17 | @override 18 | _HomeState createState() => _HomeState(); 19 | } 20 | 21 | class _HomeState extends State { 22 | int _currentPageIndex = 0; 23 | PageController _pageController; 24 | List pages = [ 25 | MainPage(), 26 | TvCategory(), 27 | DiscoverPage(), 28 | NewsPage(), 29 | ]; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | _pageController = PageController(initialPage: _currentPageIndex); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | // BlocProvider.of(context).add(ListenToLoginEvent()); 40 | return Scaffold( 41 | // backgroundColor: Colors.white, 42 | // key: homeScaffoldKey, 43 | appBar: AppBar( 44 | elevation: 0, 45 | backgroundColor: Theme.of(context).canvasColor, 46 | textTheme: Theme.of(context).textTheme, 47 | // leading: IconButton( 48 | // icon: Icon( 49 | // Icons.menu, 50 | // color: Theme.of(context).iconTheme.color, 51 | // ), 52 | // onPressed: () { 53 | // buildShowModalBottomSheet(context); 54 | // }, 55 | // ), 56 | title: Row( 57 | crossAxisAlignment: CrossAxisAlignment.center, 58 | mainAxisSize: MainAxisSize.min, 59 | children: [ 60 | Text("[", 61 | style: TextStyle( 62 | color: Theme.of(context).accentColor, 63 | fontSize: 26, 64 | fontWeight: FontWeight.bold, 65 | fontFamily: "Poppins-Bold")), 66 | Text("Movie", 67 | style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold,fontFamily: "Poppins-Bold")), 68 | Text("DB", 69 | style: TextStyle( 70 | color: Theme.of(context).accentColor, 71 | fontSize: 26, 72 | fontWeight: FontWeight.bold, 73 | fontFamily: "Poppins-Bold")), 74 | Text("]", 75 | style: TextStyle( 76 | fontSize: 26, 77 | fontWeight: FontWeight.bold, 78 | fontFamily: "Poppins-Bold")), 79 | ], 80 | ), 81 | // centerTitle: true, 82 | actions: [ 83 | IconButton( 84 | icon: Icon(Icons.search), 85 | color: Theme.of(context).iconTheme.color, 86 | onPressed: () { 87 | showSearch(context: context, delegate: SearchWidget()); 88 | }, 89 | ), 90 | BlocBuilder( 91 | bloc: BlocProvider.of(context), 92 | builder: (BuildContext context, state) { 93 | if (state is AuthLoginState) { 94 | User user = state.user; 95 | // print("object ${user.email}"); 96 | 97 | return IconButton( 98 | onPressed: () { 99 | user != null 100 | ? Navigator.of(context).push(MaterialPageRoute( 101 | builder: (BuildContext ctx) => 102 | ProfileScreen(user: user))) 103 | : 104 | // widget.user == null? 105 | // Navigator.of(context).push(MaterialPageRoute( 106 | // builder: (BuildContext ctx) => SignInPage())) 107 | showDialog( 108 | useRootNavigator: true, 109 | barrierDismissible: true, 110 | context: context, 111 | builder: (BuildContext context) { 112 | return Dialog( 113 | child: AuthModalForm(), 114 | shape: RoundedRectangleBorder( 115 | borderRadius: BorderRadius.circular(3)), 116 | ); 117 | }); 118 | }, 119 | icon: Icon( 120 | FontAwesome.user_circle_o, 121 | color: Theme.of(context).iconTheme.color, 122 | ), 123 | ); 124 | } 125 | 126 | return SizedBox.shrink(); 127 | }, 128 | ) 129 | ], 130 | ), 131 | body: Column( 132 | mainAxisAlignment: MainAxisAlignment.start, 133 | crossAxisAlignment: CrossAxisAlignment.start, 134 | children: [ 135 | // AppBarSection(), 136 | // SizedBox( 137 | // height: 10, 138 | // ), 139 | Expanded( 140 | child: PageView( 141 | scrollDirection: Axis.vertical, 142 | physics: NeverScrollableScrollPhysics(), 143 | controller: _pageController, 144 | children: pages), 145 | ) 146 | ], 147 | ), 148 | bottomNavigationBar: BottomNavigationBar( 149 | currentIndex: _currentPageIndex, 150 | type: BottomNavigationBarType.fixed, 151 | onTap: (index) { 152 | setState(() { 153 | _currentPageIndex = index; 154 | _pageController.animateToPage(_currentPageIndex, 155 | curve: Curves.easeInOutQuint, duration: Duration(seconds: 1)); 156 | }); 157 | }, 158 | items: [ 159 | BottomNavigationBarItem( 160 | icon: Icon(Icons.movie), label: "Movies"), 161 | BottomNavigationBarItem( 162 | icon: Icon(Icons.live_tv), label: "TV Series"), 163 | BottomNavigationBarItem( 164 | icon: Icon(MaterialIcons.open_in_browser), 165 | label: "Discover"), 166 | BottomNavigationBarItem( 167 | icon: Icon(FontAwesome.newspaper_o), label: "News") 168 | ], 169 | ), 170 | // floatingActionButton: _currentPageIndex == 3 171 | // ? FloatingActionButton.extended( 172 | // label: Icon(Icons.share), 173 | // icon: Text("Share watch List"), 174 | // onPressed: () { 175 | // homeScaffoldKey.currentState.showSnackBar(SnackBar(content:Text("dhgdhnk"))); 176 | // }, 177 | // ) 178 | // : SizedBox.shrink(), 179 | ); 180 | } 181 | 182 | Future buildShowModalBottomSheet(BuildContext context) { 183 | return showModalBottomSheet( 184 | shape: RoundedRectangleBorder( 185 | borderRadius: BorderRadius.only( 186 | topLeft: Radius.circular(16), topRight: Radius.circular(16))), 187 | builder: (BuildContext context) { 188 | return ModalContent(); 189 | }, 190 | context: context); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /lib/pages/loading_text_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shimmer/shimmer.dart'; 3 | 4 | class LoadingTextWidget extends StatelessWidget { 5 | 6 | final Color baseColor; 7 | final Color highlightColor; 8 | final String text; 9 | 10 | const LoadingTextWidget({ 11 | Key key, this.baseColor, this.highlightColor,this.text 12 | }) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Center( 17 | child: SizedBox( 18 | width: 200.0, 19 | height: 100.0, 20 | child: Shimmer.fromColors( 21 | baseColor: baseColor, 22 | highlightColor: highlightColor, 23 | child: Text( 24 | text, 25 | textAlign: TextAlign.center, 26 | style: TextStyle( 27 | fontSize: 24.0, 28 | fontWeight: FontWeight.bold, 29 | ), 30 | ), 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/pages/movie_credits_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:MovieDB/model/credit.dart'; 4 | import 'package:MovieDB/widgets/cast_item.dart'; 5 | import 'package:MovieDB/widgets/crew_item.dart'; 6 | 7 | class MovieCreditsPage extends StatefulWidget { 8 | final Credit credit; 9 | 10 | MovieCreditsPage({ 11 | Key key, 12 | this.credit, 13 | }) : super(key: key); 14 | 15 | @override 16 | _MovieCreditsPageState createState() => _MovieCreditsPageState(); 17 | } 18 | 19 | class _MovieCreditsPageState extends State 20 | with SingleTickerProviderStateMixin { 21 | TabController _tabController; 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | _tabController = TabController(length: 2, vsync: this); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar( 33 | title: Text("Movie Credits"), 34 | bottom: TabBar( 35 | controller: _tabController, 36 | tabs: [ 37 | Tab( 38 | text: "Casts", 39 | ), 40 | Tab( 41 | text: "Crew", 42 | ) 43 | ], 44 | ), 45 | ), 46 | body: TabBarView( 47 | controller: _tabController, 48 | children: [ 49 | CastWidget( 50 | casts: widget.credit.cast, 51 | ), 52 | CrewWidget( 53 | crew: widget.credit.crew, 54 | ), 55 | ], 56 | )); 57 | } 58 | } 59 | 60 | class CastWidget extends StatelessWidget { 61 | final List casts; 62 | 63 | CastWidget({this.casts}); 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return GridView.builder( 68 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 69 | crossAxisCount: 2, childAspectRatio: 1.0), 70 | itemCount: casts.length, 71 | shrinkWrap: true, 72 | itemBuilder: (BuildContext context, int index) { 73 | return MovieCastItem( 74 | cast: casts[index], 75 | ); 76 | }, 77 | ); 78 | } 79 | } 80 | 81 | class CrewWidget extends StatelessWidget { 82 | final List crew; 83 | 84 | CrewWidget({this.crew}); 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | return GridView.builder( 89 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 90 | crossAxisCount: 2, childAspectRatio: 1.0), 91 | itemCount: crew.length, 92 | shrinkWrap: true, 93 | itemBuilder: (BuildContext context, int index) { 94 | return MovieCrewItem( 95 | crew: crew[index], 96 | ); 97 | }, 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/pages/profile_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:flutter_icons/flutter_icons.dart'; 5 | import 'package:MovieDB/bloc/auth_bloc/auth_bloc.dart'; 6 | import 'package:MovieDB/bloc/auth_bloc/auth_event.dart'; 7 | import 'package:MovieDB/fragments/movies_category.dart'; 8 | 9 | import 'package:MovieDB/pages/favourites_page.dart'; 10 | import 'package:MovieDB/pages/watchlist_page.dart'; 11 | import 'package:getwidget/getwidget.dart'; 12 | 13 | class ProfileScreen extends StatefulWidget { 14 | final User user; 15 | ProfileScreen({ 16 | Key key, 17 | this.user, 18 | }) : super(key: key); 19 | 20 | @override 21 | _ProfileScreenState createState() => _ProfileScreenState(); 22 | } 23 | 24 | class _ProfileScreenState extends State { 25 | @override 26 | Widget build(BuildContext context) { 27 | return Scaffold( 28 | appBar: AppBar( 29 | title: Text("Profile Account"), 30 | ), 31 | body: ListView( 32 | physics: BouncingScrollPhysics(), 33 | children: [ 34 | SizedBox( 35 | height: 10, 36 | ), 37 | Center( 38 | child: GFAvatar( 39 | shape: GFAvatarShape.circle, 40 | radius: 45, 41 | backgroundImage: NetworkImage(widget.user.photoURL), 42 | ), 43 | ), 44 | SizedBox( 45 | height: 8, 46 | ), 47 | Center( 48 | child: Text('${widget.user.displayName}', 49 | style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold))), 50 | SizedBox( 51 | height: 8, 52 | ), 53 | Center( 54 | child: Text( 55 | '${widget.user.email}', 56 | style: TextStyle(fontSize: 16), 57 | )), 58 | SizedBox(height: 32.0), 59 | ListTile( 60 | contentPadding: EdgeInsets.symmetric(horizontal: 16.0), 61 | title: Text("Watch List"), 62 | leading: Icon(Icons.watch_later), 63 | trailing: Icon(Icons.keyboard_arrow_right), 64 | onTap: () => Navigator.of(context).push(MaterialPageRoute( 65 | builder: (BuildContext context) => WatchListPage( 66 | user: widget.user, 67 | ))), 68 | ), 69 | Divider(), 70 | ListTile( 71 | contentPadding: EdgeInsets.symmetric(horizontal: 16.0), 72 | title: Text("Favourites"), 73 | leading: Icon(Icons.favorite), 74 | trailing: Icon(Icons.keyboard_arrow_right), 75 | onTap: () => Navigator.of(context).push(MaterialPageRoute( 76 | builder: (BuildContext context) => FavouritesPage())), 77 | ), 78 | Divider(), 79 | ListTile( 80 | contentPadding: EdgeInsets.symmetric(horizontal: 16.0), 81 | title: Text("Logout"), 82 | leading: Icon(FontAwesome.sign_out), 83 | onTap: () { 84 | BlocProvider.of(context).add(LogoutEvent()); 85 | Navigator.pop(context); 86 | }, 87 | ), 88 | Divider(), 89 | ModalContent() 90 | ], 91 | ), 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/pages/tv_seasons_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/model/tv_details.dart'; 2 | import 'package:MovieDB/pages/tv_season_detail_page.dart'; 3 | import 'package:MovieDB/repository/constants.dart'; 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_icons/flutter_icons.dart'; 7 | 8 | class TvSeasonsList extends StatefulWidget { 9 | final TvDetails tvDetails; 10 | 11 | const TvSeasonsList({Key key, this.tvDetails}) : super(key: key); 12 | 13 | @override 14 | _TvSeasonsListState createState() => _TvSeasonsListState(); 15 | } 16 | 17 | class _TvSeasonsListState extends State { 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | appBar: AppBar( 22 | title: Text(widget.tvDetails.name), 23 | ), 24 | body: Column(children: [ 25 | Expanded( 26 | child: ListView.builder( 27 | // shrinkWrap: true, 28 | physics: BouncingScrollPhysics(), 29 | itemCount: widget.tvDetails.seasons.length, 30 | itemBuilder: (BuildContext context, int index) { 31 | Season season = widget.tvDetails.seasons[index]; 32 | return Card( 33 | clipBehavior: Clip.antiAlias, 34 | margin: EdgeInsets.all(16), 35 | elevation: 8, 36 | color: Theme.of(context).canvasColor, 37 | child: Stack(children: [ 38 | InkWell( 39 | onTap: () { 40 | Navigator.of(context).push(MaterialPageRoute( 41 | builder: (BuildContext context) => 42 | TvSeasonDetailsPage( 43 | id: widget.tvDetails.id, 44 | season: season, 45 | ))); 46 | }, 47 | child: Row( 48 | crossAxisAlignment: CrossAxisAlignment.start, 49 | children: [ 50 | Expanded( 51 | flex: 3, 52 | child: Container( 53 | margin: EdgeInsets.only( 54 | left: 16, right: 8, top: 16, bottom: 16), 55 | decoration: BoxDecoration( 56 | borderRadius: BorderRadius.circular(10)), 57 | child: ClipRRect( 58 | borderRadius: BorderRadius.circular(10), 59 | child: CachedNetworkImage( 60 | imageUrl: season.posterPath != null 61 | ? "${IMAGE_URL + season.posterPath}" 62 | : "assets/images/no-image.jpg", 63 | fit: BoxFit.cover, 64 | width: 70, 65 | height: 85, 66 | placeholder: (context, url) => Center( 67 | child: SizedBox( 68 | width: 24, 69 | height: 24, 70 | child: Center( 71 | child: CircularProgressIndicator( 72 | strokeWidth: 1, 73 | )))), 74 | errorWidget: (context, url, error) => 75 | Icon(Icons.error), 76 | ), 77 | ), 78 | ), 79 | ), 80 | SizedBox( 81 | width: 16.0, 82 | ), 83 | Expanded( 84 | flex: 8, 85 | child: Column( 86 | mainAxisAlignment: MainAxisAlignment.start, 87 | crossAxisAlignment: CrossAxisAlignment.start, 88 | children: [ 89 | Container( 90 | padding: EdgeInsets.only(bottom: 16.0), 91 | child: Row( 92 | children: [ 93 | Expanded( 94 | flex: 5, 95 | child: Row( 96 | children: [ 97 | Expanded( 98 | flex: 5, 99 | child: Padding( 100 | padding: EdgeInsets.only( 101 | top: 16,), 102 | child: Text( 103 | '${season.name} | ${season.episodeCount} Episodes', 104 | maxLines: 1, 105 | overflow: TextOverflow.ellipsis, 106 | style: TextStyle( 107 | fontWeight: 108 | FontWeight.bold), 109 | ), 110 | ), 111 | ), 112 | ], 113 | ), 114 | ), 115 | ], 116 | ), 117 | ), 118 | Padding( 119 | padding: const EdgeInsets.only(right: 16), 120 | child: Text( 121 | season.overview, 122 | style: TextStyle(color: Colors.grey), 123 | maxLines: 2, 124 | overflow: TextOverflow.ellipsis, 125 | ), 126 | ), 127 | SizedBox( 128 | height: 8, 129 | ), 130 | ], 131 | ), 132 | ), 133 | ], 134 | ), 135 | ), 136 | ]), 137 | ); 138 | }, 139 | ), 140 | ), 141 | ]), 142 | ); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/repository/auth_repository.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:cloud_firestore/cloud_firestore.dart'; 4 | import 'package:firebase_auth/firebase_auth.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:flutter_facebook_login/flutter_facebook_login.dart'; 7 | import 'package:MovieDB/model/user.dart' as NUser; 8 | import 'package:google_sign_in/google_sign_in.dart'; 9 | 10 | class AuthRepository { 11 | FirebaseAuth _auth = FirebaseAuth.instance; 12 | final CollectionReference userReference = 13 | FirebaseFirestore.instance.collection("flutter_ui_challenge"); 14 | 15 | final GoogleSignIn _googleSignIn = GoogleSignIn( 16 | scopes: [ 17 | 'email', 18 | // 'https://www.googleapis.com/auth/contacts.readonly', 19 | ], 20 | ); 21 | final facebookLogin = FacebookLogin(); 22 | 23 | NUser.User userFromFirebaseUser(User firebaseUser) { 24 | return NUser.User( 25 | id: firebaseUser.uid, 26 | email: firebaseUser.email, 27 | avatarUrl: firebaseUser.photoURL); 28 | } 29 | 30 | Future loginUser(String email, String password) async { 31 | try { 32 | var result = await _auth.signInWithEmailAndPassword( 33 | email: email, password: password); 34 | User user = result.user; 35 | if (user == null) { 36 | return null; 37 | } else { 38 | // print("You are looged in successfully..."); 39 | return userFromFirebaseUser(user); 40 | } 41 | } catch (ex) { 42 | return ex.message; 43 | // print(ex.message); 44 | } 45 | } 46 | 47 | Future loginUserWithCredentials({BuildContext context}) async { 48 | try { 49 | var googleSignInAccount = await _googleSignIn.signIn(); 50 | if (googleSignInAccount != null) { 51 | final GoogleSignInAuthentication googleSignInAuthentication = 52 | await googleSignInAccount.authentication; 53 | 54 | final AuthCredential credential = GoogleAuthProvider.credential( 55 | accessToken: googleSignInAuthentication.accessToken, 56 | idToken: googleSignInAuthentication.idToken, 57 | ); 58 | 59 | var result = await _auth.signInWithCredential(credential); 60 | User user = result.user; 61 | if (user == null) { 62 | return null; 63 | } else { 64 | // print("You are looged in successfully..."); 65 | return user; 66 | } 67 | } 68 | } catch (error) { 69 | print(error); 70 | } 71 | } 72 | 73 | Future loginUserWithFBCredentials({BuildContext context}) async { 74 | try { 75 | final result = await facebookLogin.logIn(["email"]); 76 | 77 | switch (result.status) { 78 | case FacebookLoginStatus.loggedIn: 79 | var credential = 80 | FacebookAuthProvider.credential(result.accessToken.token); 81 | var authResult = await _auth.signInWithCredential(credential); 82 | User user = authResult.user; 83 | if (user == null) { 84 | return null; 85 | } else { 86 | // print("You are looged in successfully..."); 87 | return user; 88 | } 89 | // _sendTokenToServer(result.accessToken.token); 90 | // _showLoggedInUI(); 91 | break; 92 | case FacebookLoginStatus.cancelledByUser: 93 | // _showCancelledMessage(); 94 | break; 95 | case FacebookLoginStatus.error: 96 | // _showErrorOnUI(result.errorMessage); 97 | break; 98 | } 99 | } catch (error) { 100 | print(error); 101 | } 102 | } 103 | 104 | Future registerUser( 105 | String email, String fname, String lname, String password) async { 106 | try { 107 | var result = await _auth.createUserWithEmailAndPassword( 108 | email: email, password: password); 109 | User user = result.user; 110 | if (user == null) { 111 | return null; 112 | } else { 113 | // print("You have been registered successfully..."); 114 | var savedUser = _saveUserToDatabase(NUser.User( 115 | id: user.uid, 116 | email: email, 117 | firstname: fname, 118 | lastname: lname, 119 | avatarUrl: user.photoURL != null ? user.photoURL : "")); 120 | 121 | if (savedUser == null) { 122 | return null; 123 | } 124 | 125 | return NUser.User( 126 | id: user.uid, 127 | email: email, 128 | firstname: fname, 129 | lastname: lname, 130 | avatarUrl: user.photoURL != null ? user.photoURL : ""); 131 | } 132 | } catch (ex) { 133 | return ex.message; 134 | // print(ex.message); 135 | } 136 | } 137 | 138 | Future _saveUserToDatabase(NUser.User user) async { 139 | return await userReference.doc("data").collection("users").add({ 140 | "id": user.id, 141 | "email": user.email, 142 | "firstname": user.firstname, 143 | "lastname": user.lastname, 144 | "avatarUrl": user.avatarUrl 145 | }); 146 | // .whenComplete(()=>"").catchError((doc)=>doc.message); 147 | } 148 | 149 | Stream listenToSignIn() { 150 | return _auth.authStateChanges(); 151 | } 152 | 153 | Future logout() async { 154 | try { 155 | // if(_googleSignIn!=null){ 156 | _googleSignIn?.signOut(); 157 | facebookLogin?.logOut(); 158 | // } 159 | return _auth.signOut(); 160 | } catch (ex) { 161 | print(ex); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/repository/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | final String API_KEY = "3189670a5af406da03f513c311f29341"; 4 | final String MOVIE_BASE_URL = "https://api.themoviedb.org/3/movie/"; 5 | final String TV_BASE_URL = "https://api.themoviedb.org/3/tv/"; 6 | final TMDB_URL = "https://api.themoviedb.org/3/"; 7 | final String IMAGE_URL = "https://image.tmdb.org/t/p/w600_and_h900_bestv2"; 8 | final String THEME_PREF_KEY = 'theme_key'; 9 | final String IMAGE_TEMP_URL = 10 | 'https://firebasestorage.googleapis.com/v0/b/flutter-moviedb-ab628.appspot.com/o/1783.jpg?alt=media&token=f1e827c8-4726-4076-9969-d9abc8a858cc'; 11 | 12 | GlobalKey homeScaffoldKey = GlobalKey(); 13 | 14 | ThemeData getLightTheme(BuildContext context) { 15 | return Theme.of(context).copyWith( 16 | brightness: Brightness.light, 17 | primaryColor: Colors.blue, 18 | ); 19 | } 20 | 21 | ThemeData getDarkTheme(BuildContext context) { 22 | return Theme.of(context).copyWith( 23 | brightness: Brightness.dark, 24 | primaryColor: Colors.blue, 25 | ); 26 | } 27 | 28 | enum MediaType { MOVIE, TV } 29 | -------------------------------------------------------------------------------- /lib/widgets/auth_modal_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_icons/flutter_icons.dart'; 3 | import 'package:MovieDB/bloc/auth_bloc/auth_bloc.dart'; 4 | import 'package:MovieDB/bloc/auth_bloc/auth_event.dart'; 5 | 6 | class AuthModalForm extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Container( 10 | height: MediaQuery.of(context).size.height*0.5, 11 | padding: EdgeInsets.all(16), 12 | child: Column( 13 | crossAxisAlignment: CrossAxisAlignment.center, 14 | children: [ 15 | SizedBox(height: 16,), 16 | Text( 17 | "Use one of the following sign in options to enjoy the full previledge of MovieDB", 18 | textAlign: TextAlign.center, 19 | style: TextStyle( 20 | fontSize: 18, 21 | ), 22 | ), 23 | SizedBox( 24 | height: 10.0, 25 | ), 26 | Divider( 27 | color: Theme.of(context).accentColor, 28 | height: 2.0, 29 | ), 30 | SizedBox( 31 | height: 10.0, 32 | ), 33 | RaisedButton.icon( 34 | color: Colors.blueAccent, 35 | icon: Icon( 36 | FontAwesome.google, 37 | color: Colors.white, 38 | ), 39 | label: Text("SIGN IN WITH GOOGLE"), 40 | onPressed: () { 41 | new AuthBloc().add(GoogleLoginEvent()); 42 | Navigator.pop(context); 43 | }, 44 | ), 45 | SizedBox( 46 | height: 10.0, 47 | ), 48 | RaisedButton.icon( 49 | icon: Icon( 50 | FontAwesome.facebook, 51 | color: Colors.white, 52 | ), 53 | label: Text("SIGN IN WITH FACEBOOK"), 54 | color: Colors.blue, 55 | onPressed: () { 56 | new AuthBloc().add(FacebookLoginEvent()); 57 | Navigator.pop(context); 58 | }, 59 | ) 60 | ], 61 | )); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/widgets/cast_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:MovieDB/model/credit.dart'; 3 | import 'package:MovieDB/pages/person_details_page.dart'; 4 | import 'package:MovieDB/repository/constants.dart'; 5 | 6 | class MovieCastItem extends StatelessWidget { 7 | const MovieCastItem({ 8 | Key key, 9 | @required this.cast, 10 | }) : super(key: key); 11 | 12 | final Cast cast; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | // print('${IMAGE_URL + cast.profilePath}'); 17 | 18 | return InkWell( 19 | borderRadius: BorderRadius.circular(10), 20 | onTap: () { 21 | Navigator.of(context) 22 | .push(MaterialPageRoute(builder: (BuildContext context)=>PersonDetailsPage(id:cast.id))); 23 | }, 24 | child: Container( 25 | margin: EdgeInsets.all(10.0), 26 | // height: 250, 27 | // width: 150, 28 | decoration: BoxDecoration( 29 | // color: Colors.white, 30 | borderRadius: BorderRadius.circular(10), 31 | ), 32 | child: Column( 33 | mainAxisAlignment: MainAxisAlignment.center, 34 | crossAxisAlignment: CrossAxisAlignment.center, 35 | children: [ 36 | Expanded( 37 | flex: 9, 38 | child: Center( 39 | child: cast.profilePath != null ?CircleAvatar( 40 | backgroundImage: 41 | NetworkImage(cast.profilePath != null ? '${IMAGE_URL + cast.profilePath}':IMAGE_TEMP_URL), 42 | radius: 50, 43 | ):CircleAvatar(child: Icon(Icons.person,size: 90,color: Colors.white24,),radius: 50,backgroundColor: Colors.grey,) 44 | ) 45 | ), 46 | Expanded( 47 | flex: 6, 48 | child: Padding( 49 | padding: const EdgeInsets.symmetric(vertical: 8.0), 50 | child: SizedBox( 51 | width: 130, 52 | child: Column( 53 | crossAxisAlignment: CrossAxisAlignment.center, 54 | children: [ 55 | Text( 56 | '${cast.name}', 57 | style: TextStyle(fontWeight: FontWeight.w500), 58 | maxLines: 1, 59 | overflow: TextOverflow.ellipsis, 60 | ), 61 | Text( 62 | '(${cast.character})', 63 | style: TextStyle( 64 | color: Colors.grey, fontStyle: FontStyle.italic), 65 | maxLines: 1, 66 | overflow: TextOverflow.ellipsis, 67 | ), 68 | ], 69 | ), 70 | ), 71 | )), 72 | ], 73 | ), 74 | ), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/widgets/cast_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:MovieDB/model/credit.dart'; 3 | import 'package:MovieDB/pages/movie_credits_page.dart'; 4 | import 'package:MovieDB/widgets/cast_item.dart'; 5 | 6 | class CastList extends StatefulWidget { 7 | final String title; 8 | final Credit credit; 9 | 10 | CastList({Key key, this.title, this.credit}) : super(key: key); 11 | 12 | @override 13 | _MovieCastList createState() => _MovieCastList(); 14 | } 15 | 16 | class _MovieCastList extends State { 17 | // List cast= widget.credit.cast; 18 | 19 | @override 20 | void initState() { 21 | // cast = widget.credit.cast; 22 | super.initState(); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Card( 28 | margin: EdgeInsets.only(top: 8), 29 | elevation: 5, 30 | color: Theme.of(context).canvasColor, 31 | child: Container( 32 | margin: EdgeInsets.only(top: 8), 33 | height: 240, 34 | // width: 200, 35 | child: Column( 36 | crossAxisAlignment: CrossAxisAlignment.start, 37 | children: [ 38 | Padding( 39 | padding: const EdgeInsets.symmetric(horizontal: 16.0), 40 | child: Row( 41 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 42 | children: [ 43 | Text( 44 | widget.title, 45 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), 46 | ), 47 | GestureDetector( 48 | onTap: () => Navigator.of(context).push(MaterialPageRoute( 49 | builder: (context) => MovieCreditsPage( 50 | credit: widget.credit, 51 | ))), 52 | child: Container( 53 | padding: EdgeInsets.symmetric(horizontal: 8, vertical: 5), 54 | decoration: BoxDecoration( 55 | color: Theme.of(context).accentColor.withOpacity(0.3), 56 | borderRadius: BorderRadius.circular(16.0)), 57 | child: Center( 58 | child: Text( 59 | "View All", 60 | style: TextStyle( 61 | fontWeight: FontWeight.w500, 62 | fontSize: 14, 63 | color: Theme.of(context).accentColor), 64 | ), 65 | ), 66 | ), 67 | ), 68 | ], 69 | ), 70 | ), 71 | Expanded( 72 | 73 | child:widget.credit.cast!=null ? ListView.builder( 74 | physics: BouncingScrollPhysics(), 75 | itemCount: widget.credit.cast.length, 76 | // shrinkWrap: true, 77 | scrollDirection: Axis.horizontal, 78 | itemBuilder: (BuildContext context, int index) { 79 | // print(movies[index].title); 80 | // Movie movie=movies[index]; 81 | var cast = widget.credit.cast; 82 | return MovieCastItem( 83 | cast: cast[index], 84 | ); 85 | }, 86 | ):SizedBox.shrink()), 87 | ], 88 | ), 89 | ), 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/widgets/crew_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:MovieDB/model/credit.dart'; 3 | import 'package:MovieDB/pages/person_details_page.dart'; 4 | import 'package:MovieDB/repository/constants.dart'; 5 | 6 | class MovieCrewItem extends StatelessWidget { 7 | const MovieCrewItem({ 8 | Key key, 9 | @required this.crew, 10 | }) : super(key: key); 11 | 12 | final Crew crew; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | // print('${IMAGE_URL + cast.profilePath}'); 17 | 18 | return InkWell( 19 | borderRadius: BorderRadius.circular(10), 20 | onTap: () { 21 | Navigator.of(context) 22 | .push(MaterialPageRoute(builder: (BuildContext context)=>PersonDetailsPage(id:crew.id))); 23 | }, 24 | child: Container( 25 | margin: EdgeInsets.all(10.0), 26 | // height: 250, 27 | // width: 150, 28 | decoration: BoxDecoration( 29 | // color: Colors.white, 30 | borderRadius: BorderRadius.circular(10), 31 | ), 32 | child: Column( 33 | mainAxisAlignment: MainAxisAlignment.center, 34 | crossAxisAlignment: CrossAxisAlignment.center, 35 | children: [ 36 | Expanded( 37 | flex: 9, 38 | child: Center( 39 | child: crew.profilePath != null ?CircleAvatar( 40 | backgroundImage: 41 | NetworkImage(crew.profilePath != null ? '${IMAGE_URL + crew.profilePath}': IMAGE_TEMP_URL), 42 | radius: 50, 43 | ):CircleAvatar(child: Icon(Icons.person,size: 90,color: Colors.white24,),radius: 50,backgroundColor: Colors.grey,) 44 | ) 45 | ), 46 | Expanded( 47 | flex: 6, 48 | child: Padding( 49 | padding: const EdgeInsets.symmetric(vertical: 8.0), 50 | child: SizedBox( 51 | width: 130, 52 | child: Column( 53 | crossAxisAlignment: CrossAxisAlignment.center, 54 | children: [ 55 | Text( 56 | '${crew.name}', 57 | style: TextStyle(fontWeight: FontWeight.w500), 58 | maxLines: 1, 59 | overflow: TextOverflow.ellipsis, 60 | ), 61 | Text( 62 | '(${crew.job})', 63 | style: TextStyle( 64 | color: Colors.grey, fontStyle: FontStyle.italic), 65 | maxLines: 1, 66 | overflow: TextOverflow.ellipsis, 67 | ), 68 | ], 69 | ), 70 | ), 71 | )), 72 | ], 73 | ), 74 | ), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/widgets/curved_path_bg.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class CurvedPath extends CustomClipper{ 4 | @override 5 | Path getClip(Size size) { 6 | // final path = Path(); 7 | // path.lineTo(size.width, 0.0); 8 | // path.lineTo(size.width, size.height*0.6); 9 | // path.lineTo(size.width*0.5, size.height*0.95); 10 | // path.quadraticBezierTo(0.8, size.height,size.width*0.3, size.height*0.3); 11 | // path.close(); 12 | 13 | var path = new Path(); 14 | path.lineTo(0.0, size.height * 0.6); 15 | 16 | var firstControlPoint = Offset(size.width / 4, size.height); 17 | var firstEndPoint = Offset(size.width / 2.25, size.height ); 18 | path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy, 19 | firstEndPoint.dx, firstEndPoint.dy); 20 | 21 | var secondControlPoint = 22 | Offset(size.width *0.9, size.height *0.99); 23 | var secondEndPoint = Offset(size.width, size.height*0.55); 24 | path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy, 25 | secondEndPoint.dx, secondEndPoint.dy); 26 | 27 | path.lineTo(size.width, size.height - 40); 28 | path.lineTo(size.width, 0.0); 29 | path.close(); 30 | 31 | return path; 32 | 33 | } 34 | 35 | @override 36 | bool shouldReclip(CustomClipper oldClipper) { 37 | return true; 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /lib/widgets/custom_path_clipper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CustomTagClipper extends CustomClipper{ 4 | 5 | @override 6 | bool shouldReclip(CustomClipper oldClipper) { 7 | return true; 8 | } 9 | 10 | @override 11 | getClip(Size size) { 12 | double width=size.width; 13 | double height=size.height; 14 | 15 | Path path=Path(); 16 | path.lineTo(0, height); 17 | path.lineTo(width*0.5, height*0.8); 18 | path.lineTo(width*0.5, height*0.8); 19 | path.lineTo(width, height); 20 | path.lineTo(width, 0); 21 | path.close(); 22 | return path; 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /lib/widgets/image_list_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/pages/loading_text_widget.dart'; 2 | import 'package:cached_network_image/cached_network_image.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:MovieDB/bloc/movies_bloc/bloc.dart'; 6 | import 'package:MovieDB/model/person_images.dart'; 7 | import 'package:MovieDB/repository/constants.dart'; 8 | import 'package:shimmer/shimmer.dart'; 9 | 10 | // class ImageList extends StatefulWidget { 11 | // final int id; 12 | 13 | // ImageList({Key key, this.id}) : super(key: key); 14 | 15 | // @override 16 | // _ImageListState createState() => _ImageListState(); 17 | // } 18 | 19 | class ImageList extends StatefulWidget { 20 | final int id; 21 | 22 | ImageList({this.id}); 23 | 24 | @override 25 | _ImageListState createState() => _ImageListState(); 26 | } 27 | 28 | class _ImageListState extends State { 29 | MoviesBloc _moviesBloc = MoviesBloc(); 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | _moviesBloc.add(GetPersonImagesEvent(id: widget.id)); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return Container( 40 | height: 150, 41 | // width: 200, 42 | child: BlocBuilder( 43 | bloc: _moviesBloc, 44 | builder: (BuildContext context, MoviesState state) { 45 | if (state is MovieLoadingState) { 46 | return Center(child: LoadingTextWidget(baseColor: Colors.red,highlightColor: Colors.yellow,text: "Loading...",)); 47 | } 48 | if (state is PersonImagesState) { 49 | var personImages = state.images; 50 | var images = personImages.profiles; 51 | if (images != null) { 52 | if (images.length > 0) { 53 | return ListView.builder( 54 | physics: BouncingScrollPhysics(), 55 | itemCount: images.length, 56 | // shrinkWrap: true, 57 | scrollDirection: Axis.horizontal, 58 | itemBuilder: (BuildContext context, int index) { 59 | // print(movies[index].title); 60 | // Movie movie=movies[index]; 61 | return _buildImage(images[index]); 62 | }, 63 | ); 64 | } 65 | } else { 66 | return Center( 67 | child: Column( 68 | mainAxisAlignment: MainAxisAlignment.center, 69 | children: [ 70 | Text("Nothing was found!"), 71 | RaisedButton( 72 | child: Text("Retry"), 73 | onPressed: () => 74 | _moviesBloc.add(GetPersonImagesEvent(id: widget.id)), 75 | ) 76 | ], 77 | )); 78 | } 79 | } 80 | 81 | return Center( 82 | child: Column( 83 | mainAxisAlignment: MainAxisAlignment.center, 84 | children: [ 85 | Text("An error occured!"), 86 | RaisedButton( 87 | child: Text("Retry"), 88 | onPressed: () => _moviesBloc.add(GetPersonImagesEvent(id: widget.id)), 89 | ) 90 | ], 91 | )); 92 | }, 93 | ), 94 | ); 95 | } 96 | 97 | Widget _buildImage(Profiles image) { 98 | return Container( 99 | margin: EdgeInsets.symmetric(horizontal: 8), 100 | width: 120, 101 | height: 150, 102 | decoration: BoxDecoration( 103 | borderRadius: BorderRadius.circular(10.0), 104 | ), 105 | child: CachedNetworkImage( 106 | imageUrl: image.filePath != null ? "${IMAGE_URL + image.filePath}" : IMAGE_TEMP_URL, 107 | fit: BoxFit.cover, 108 | placeholder: (context, url) => Center( 109 | child: Shimmer.fromColors( 110 | child: Container( 111 | height: 150, 112 | width: 120, 113 | ), 114 | baseColor: Colors.grey[600], 115 | highlightColor: Colors.grey[700])), 116 | errorWidget: (context, url, error) => Icon(Icons.error), 117 | ), 118 | ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib/widgets/loading_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadingButton extends StatelessWidget { 4 | 5 | final String text; 6 | LoadingButton({ 7 | Key key, 8 | this.text, 9 | }) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container( 14 | padding: EdgeInsets.symmetric(vertical: 16), 15 | decoration: BoxDecoration(color: Colors.blue[300]), 16 | child: Center( 17 | child: Row( 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | children: [ 20 | SizedBox( 21 | height: 16, 22 | width: 16, 23 | child: CircularProgressIndicator( 24 | backgroundColor: Colors.white, 25 | ), 26 | ), 27 | SizedBox( 28 | width: 16, 29 | ), 30 | Text( 31 | text, 32 | style: TextStyle(color: Colors.white, fontWeight: FontWeight.w500), 33 | ), 34 | ], 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/widgets/movie_list_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/bloc/auth_bloc/auth_bloc.dart'; 2 | import 'package:MovieDB/bloc/auth_bloc/bloc.dart'; 3 | import 'package:MovieDB/bloc/movies_bloc/movies_bloc.dart'; 4 | import 'package:MovieDB/bloc/movies_bloc/movies_event.dart'; 5 | import 'package:MovieDB/bloc/movies_bloc/movies_state.dart'; 6 | import 'package:MovieDB/pages/loading_text_widget.dart'; 7 | import 'package:MovieDB/pages/movie_list_page.dart'; 8 | import 'package:MovieDB/repository/movie_repository.dart'; 9 | import 'package:MovieDB/widgets/movie_item_horizontal.dart'; 10 | import 'package:firebase_auth/firebase_auth.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter_bloc/flutter_bloc.dart'; 13 | 14 | class MovieListRow extends StatefulWidget { 15 | final String title; 16 | final MovieCat type; 17 | final int id; 18 | final User user; 19 | 20 | MovieListRow({Key key, this.title, this.type, this.id, this.user}) 21 | : super(key: key); 22 | 23 | @override 24 | _MovieListRowState createState() => _MovieListRowState(); 25 | } 26 | 27 | class _MovieListRowState extends State { 28 | MoviesBloc _moviesBloc = MoviesBloc(); 29 | @override 30 | void initState() { 31 | _moviesBloc.add(LoadMoviesEvent( 32 | type: widget.type, 33 | id: widget.id, 34 | )); 35 | super.initState(); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Card( 41 | margin: EdgeInsets.only(top: 8.0, bottom: 8.0), 42 | color: Theme.of(context).canvasColor, 43 | elevation: 4, 44 | child: Container( 45 | padding: EdgeInsets.only(top: 8, bottom: 8), 46 | height: 320, 47 | // width: 200, 48 | // decoration: BoxDecoration( 49 | // boxShadow: [ 50 | // BoxShadow( 51 | // offset: Offset(0, 0), 52 | // blurRadius: 2, 53 | // spreadRadius: 1, 54 | // ) 55 | // ] 56 | // ), 57 | child: Column( 58 | crossAxisAlignment: CrossAxisAlignment.start, 59 | children: [ 60 | Padding( 61 | padding: const EdgeInsets.symmetric(horizontal: 16.0), 62 | child: Row( 63 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 64 | children: [ 65 | Text( 66 | widget.title, 67 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), 68 | ), 69 | InkWell( 70 | onTap: () { 71 | Navigator.of(context).push(MaterialPageRoute( 72 | builder: (BuildContext context) => MoviesListPage( 73 | title: widget.title, 74 | id: widget.id, 75 | user: widget.user, 76 | type: widget.type, 77 | ))); 78 | }, 79 | borderRadius: BorderRadius.circular(16.0), 80 | child: Container( 81 | padding: EdgeInsets.symmetric(horizontal: 8, vertical: 5), 82 | decoration: BoxDecoration( 83 | color: Theme.of(context).accentColor.withOpacity(0.3), 84 | borderRadius: BorderRadius.circular(16.0)), 85 | child: Center( 86 | child: Text( 87 | "View All", 88 | style: TextStyle( 89 | fontWeight: FontWeight.w500, 90 | fontSize: 14, 91 | color: Theme.of(context).accentColor), 92 | ), 93 | ), 94 | ), 95 | ), 96 | ], 97 | ), 98 | ), 99 | Expanded( 100 | child: BlocBuilder( 101 | builder: (BuildContext context, state) { 102 | if (state is AuthLoginState) { 103 | return buildMovieList(); 104 | } 105 | return SizedBox.shrink(); 106 | }, 107 | ), 108 | ), 109 | ], 110 | ), 111 | ), 112 | ); 113 | } 114 | 115 | Widget buildMovieList() { 116 | return BlocBuilder( 117 | bloc: _moviesBloc, 118 | builder: (BuildContext context, MoviesState state) { 119 | if (state is MovieLoadingState) { 120 | return Center( 121 | child: LoadingTextWidget( 122 | baseColor: Colors.red, 123 | highlightColor: Colors.yellow, 124 | text: "Loading...", 125 | )); 126 | } 127 | 128 | if (state is AuthErrorState) { 129 | return Center( 130 | child: Column( 131 | crossAxisAlignment: CrossAxisAlignment.center, 132 | mainAxisAlignment: MainAxisAlignment.center, 133 | children: [ 134 | Text("An error occured!"), 135 | RaisedButton( 136 | child: Text("Retry"), 137 | onPressed: () => _moviesBloc 138 | .add(LoadMoviesEvent(id: widget.id, type: widget.type)), 139 | ) 140 | ], 141 | )); 142 | } 143 | 144 | if (state is MoviesLoadedState) { 145 | var movieList = state.movies; 146 | var movies = movieList.results; 147 | if (movies != null) { 148 | if (movies.length > 0) { 149 | if (movies.length > 10) { 150 | movies = movies.sublist(0, 8); 151 | } 152 | return ListView.builder( 153 | physics: BouncingScrollPhysics(), 154 | itemCount: movies.length, 155 | addAutomaticKeepAlives: true, 156 | // shrinkWrap: true, 157 | scrollDirection: Axis.horizontal, 158 | itemBuilder: (BuildContext context, int index) { 159 | // print(movies[index].title); 160 | // Movie movie=movies[index]; 161 | return MovieItemHorizontal( 162 | movie: movies[index], 163 | user: widget.user, 164 | ); 165 | }, 166 | ); 167 | } 168 | } else { 169 | return Center( 170 | child: Column( 171 | mainAxisAlignment: MainAxisAlignment.center, 172 | children: [ 173 | Text("Nothing was found!"), 174 | RaisedButton( 175 | child: Text("Retry"), 176 | onPressed: () => _moviesBloc 177 | .add(LoadMoviesEvent(id: widget.id, type: widget.type)), 178 | ) 179 | ], 180 | )); 181 | } 182 | } 183 | 184 | return Center( 185 | child: Column( 186 | mainAxisAlignment: MainAxisAlignment.center, 187 | children: [ 188 | Text("An error occured!"), 189 | RaisedButton( 190 | child: Text("Retry"), 191 | onPressed: () => _moviesBloc 192 | .add(LoadMoviesEvent(id: widget.id, type: widget.type)), 193 | ) 194 | ], 195 | )); 196 | }, 197 | ); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /lib/widgets/movie_review_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/bloc/movies_bloc/bloc.dart'; 2 | import 'package:MovieDB/model/movie_details.dart'; 3 | import 'package:MovieDB/model/movie_review.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 7 | import 'package:getwidget/getwidget.dart'; 8 | // import 'package:getflutter/getflutter.dart'; 9 | 10 | class MovieReviewWidget extends StatefulWidget { 11 | final MovieDetails movieDetails; 12 | 13 | const MovieReviewWidget({Key key, this.movieDetails}) : super(key: key); 14 | 15 | @override 16 | _MovieReviewWidgetState createState() => _MovieReviewWidgetState(); 17 | } 18 | 19 | class _MovieReviewWidgetState extends State { 20 | 21 | MoviesBloc bloc = MoviesBloc(); 22 | 23 | @override 24 | void initState() { 25 | bloc.add(GetMovieReviews(movieId: widget.movieDetails.id)); 26 | super.initState(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | // print("Id:${movieDetails.id}"); 32 | return GFAccordion( 33 | title: "Reviews", 34 | contentBackgroundColor:Theme.of(context).canvasColor, 35 | textStyle: TextStyle( 36 | color: Colors.black, fontWeight: FontWeight.bold, fontSize: 18), 37 | collapsedIcon: Icon( 38 | Icons.keyboard_arrow_down, 39 | color: Colors.black, 40 | ), 41 | expandedIcon: Icon( 42 | Icons.keyboard_arrow_up, 43 | color: Colors.black, 44 | ), 45 | contentChild: BlocBuilder( 46 | bloc: bloc, 47 | builder: (BuildContext context, MoviesState state) { 48 | if (state is LoadingState) { 49 | print("Loading"); 50 | return Center( 51 | child: SpinKitRipple( 52 | color: Theme.of(context).accentColor, 53 | ), 54 | ); 55 | } 56 | 57 | if (state is MovieReviewsLoaded) { 58 | return _buildReviewsList(state.movieReview, context); 59 | } 60 | 61 | return SizedBox.shrink(); 62 | }, 63 | ), 64 | ); 65 | } 66 | 67 | Widget _buildReviewsList(MovieReview movieReview, BuildContext context) { 68 | List review = movieReview.results; 69 | if (movieReview.results.length > 5) { 70 | review = review.sublist(0, 5); 71 | } 72 | return Column( 73 | children: [ 74 | ListView.builder( 75 | scrollDirection: Axis.vertical, 76 | itemCount: review.length, 77 | physics: NeverScrollableScrollPhysics(), 78 | shrinkWrap: true, 79 | itemBuilder: (BuildContext context, int index) { 80 | return ReviewItemWidget(context: context, result: review[index]); 81 | }, 82 | ), 83 | movieReview.results.length > 3 84 | ? FlatButton( 85 | onPressed: () { 86 | Navigator.of(context).push(MaterialPageRoute( 87 | builder: (BuildContext context) => FullMovieReview( 88 | title: widget.movieDetails.title, 89 | reviews: movieReview, 90 | ))); 91 | }, 92 | textColor: Theme.of(context).accentColor, 93 | child: Text("See all Reviews...")) 94 | : SizedBox.shrink(), 95 | ], 96 | ); 97 | } 98 | } 99 | 100 | class ReviewItemWidget extends StatefulWidget { 101 | const ReviewItemWidget({ 102 | Key key, 103 | @required this.context, 104 | @required this.result, 105 | }) : super(key: key); 106 | 107 | final BuildContext context; 108 | final Result result; 109 | 110 | @override 111 | _ReviewItemWidgetState createState() => _ReviewItemWidgetState(); 112 | } 113 | 114 | class _ReviewItemWidgetState extends State { 115 | bool readMore=false; 116 | @override 117 | Widget build(BuildContext context) { 118 | return Container( 119 | margin: EdgeInsets.only(bottom: 16), 120 | child: ListTile( 121 | onTap: (){ 122 | setState(() { 123 | readMore=!readMore; 124 | }); 125 | }, 126 | leading: CircleAvatar( 127 | radius: 24, 128 | backgroundColor: Theme.of(context).accentColor, 129 | child: Center( 130 | child: Text( 131 | widget.result.author.substring(0, 1), 132 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), 133 | ), 134 | ), 135 | ), 136 | title: Text( 137 | widget.result.author, 138 | style: TextStyle(fontWeight: FontWeight.bold), 139 | ), 140 | subtitle: Text( 141 | widget.result.content, 142 | maxLines: readMore?1000:3, 143 | overflow: TextOverflow.ellipsis, 144 | ), 145 | ), 146 | ); 147 | } 148 | } 149 | 150 | class FullMovieReview extends StatelessWidget { 151 | final String title; 152 | final MovieReview reviews; 153 | 154 | const FullMovieReview({Key key, this.title, this.reviews}) : super(key: key); 155 | 156 | @override 157 | Widget build(BuildContext context) { 158 | return Scaffold( 159 | appBar: AppBar( 160 | title: Text('$title:Reviews'), 161 | ), 162 | body: Column( 163 | children: [ 164 | SizedBox(height: 16.0,), 165 | Expanded( 166 | child: ListView.builder( 167 | scrollDirection: Axis.vertical, 168 | itemCount: reviews.results.length, 169 | physics: BouncingScrollPhysics(), 170 | shrinkWrap: true, 171 | itemBuilder: (BuildContext context, int index) { 172 | return ReviewItemWidget( 173 | context: context, result: reviews.results[index]); 174 | }, 175 | ), 176 | ), 177 | ], 178 | )); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /lib/widgets/search_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:MovieDB/model/movie.dart'; 3 | import 'package:MovieDB/pages/movie_list_page.dart'; 4 | import 'package:MovieDB/repository/movie_repository.dart'; 5 | 6 | class SearchWidget extends SearchDelegate { 7 | 8 | @override 9 | String get searchFieldLabel => "Search Movie"; 10 | 11 | @override 12 | ThemeData appBarTheme(BuildContext context) { 13 | return Theme.of(context); 14 | } 15 | 16 | @override 17 | List buildActions(BuildContext context) { 18 | return [ 19 | IconButton( 20 | icon: Icon(Icons.clear), 21 | onPressed: () { 22 | query=''; 23 | }, 24 | ) 25 | ]; 26 | } 27 | 28 | @override 29 | Widget buildLeading(BuildContext context) { 30 | return IconButton( 31 | icon: Icon(Icons.arrow_back), 32 | onPressed: () { 33 | close(context, null); 34 | }, 35 | ); 36 | } 37 | 38 | @override 39 | Widget buildResults(BuildContext context) { 40 | return FutureBuilder( 41 | future: new MovieRepository().searchMovie(query), 42 | builder: (BuildContext context, AsyncSnapshot snapshot) { 43 | 44 | if (snapshot.data == null) { 45 | return Center( 46 | child: CircularProgressIndicator(), 47 | ); 48 | } 49 | return MovieListLayout( 50 | // type: widget.type, 51 | // id: widget.id, 52 | movieList: snapshot.data, 53 | isVertical: true, 54 | ); 55 | }, 56 | ); 57 | } 58 | 59 | @override 60 | Widget buildSuggestions(BuildContext context) { 61 | return Container(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /lib/widgets/trailers_row_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:MovieDB/model/video_details.dart'; 4 | import 'package:MovieDB/repository/constants.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:youtube_player_flutter/youtube_player_flutter.dart'; 7 | 8 | class TrailersVideoRow extends StatelessWidget { 9 | final List videos; 10 | 11 | TrailersVideoRow({ 12 | Key key, 13 | this.videos, 14 | }) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return ListView.builder( 19 | itemCount: videos.length, 20 | scrollDirection: Axis.horizontal, 21 | itemBuilder: (BuildContext context, int index) { 22 | String thumbnail = 23 | YoutubePlayer.getThumbnail(videoId: videos[index].key); 24 | // print("=" + thumbnail); 25 | return Card( 26 | margin: EdgeInsets.symmetric(horizontal: 16), 27 | elevation: 8, 28 | clipBehavior: Clip.antiAlias, 29 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | child: InkWell( 31 | onTap: () { 32 | _showVideoDialog(context, videos[index]); 33 | }, 34 | child: Container( 35 | width: 250, 36 | child: Column( 37 | crossAxisAlignment: CrossAxisAlignment.start, 38 | children: [ 39 | Expanded( 40 | flex: 4, 41 | child: Stack(children: [ 42 | Image.network( 43 | thumbnail ?? IMAGE_TEMP_URL, 44 | width: 250, 45 | fit: BoxFit.cover, 46 | ), 47 | Align( 48 | alignment: Alignment.center, 49 | child: Container( 50 | width: 50, 51 | height: 50, 52 | decoration: 53 | BoxDecoration(color: Colors.black.withOpacity(0.5)), 54 | child: Center( 55 | child: Icon( 56 | Icons.play_arrow, 57 | color: Colors.white, 58 | ), 59 | ), 60 | ), 61 | ) 62 | ]), 63 | ), 64 | SizedBox( 65 | height: 8, 66 | ), 67 | Expanded( 68 | flex: 2, 69 | child: Padding( 70 | padding: const EdgeInsets.all(8.0), 71 | child: Text( 72 | videos[index].name, 73 | maxLines: 2, 74 | ), 75 | )), 76 | ], 77 | ), 78 | ), 79 | ), 80 | ); 81 | }, 82 | ); 83 | } 84 | 85 | void _showVideoDialog(BuildContext context, Results video) { 86 | showDialog( 87 | context: context, 88 | builder: (BuildContext context)=> BackdropFilter( 89 | filter: ImageFilter.blur(sigmaX: 7, sigmaY: 7), 90 | child: SimpleDialog( 91 | title: Text( 92 | video.name, 93 | style: TextStyle(color: Colors.white), 94 | ), 95 | children: [ 96 | Row( 97 | children: [ 98 | ], 99 | ), 100 | TrailerPlayer( 101 | video: video, 102 | ), 103 | ], 104 | backgroundColor: Colors.black, 105 | ), 106 | )); 107 | } 108 | } 109 | 110 | class TrailerPlayer extends StatefulWidget { 111 | final Results video; 112 | 113 | TrailerPlayer({ 114 | Key key, 115 | this.video, 116 | }) : super(key: key); 117 | 118 | @override 119 | _TrailerPlayerState createState() => _TrailerPlayerState(); 120 | } 121 | 122 | class _TrailerPlayerState extends State { 123 | YoutubePlayerController _controller; 124 | 125 | @override 126 | void initState() { 127 | _controller = YoutubePlayerController( 128 | initialVideoId: widget.video.key, 129 | flags: YoutubePlayerFlags( 130 | mute: false, 131 | ), 132 | ); 133 | super.initState(); 134 | } 135 | 136 | @override 137 | Widget build(BuildContext context) { 138 | return Container( 139 | height: MediaQuery.of(context).size.height * 0.4, 140 | child: YoutubePlayer( 141 | aspectRatio: 0.4, 142 | controller: _controller, 143 | // showVideoProgressIndicator: true, 144 | topActions: [], 145 | onEnded: (c) { 146 | // _controller.reload(); 147 | }, 148 | onReady: () { 149 | // print('ready'); 150 | 151 | _controller.addListener(() {}); 152 | }, 153 | // progressColors: ProgressBarColors( 154 | // playedColor: Colors.amber, 155 | // handleColor: Colors.amberAccent, 156 | // ), 157 | ), 158 | ); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /lib/widgets/tv_item_horizontal.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/bloc/movies_bloc/bloc.dart'; 2 | import 'package:MovieDB/bloc/tv_bloc/tv_bloc.dart'; 3 | import 'package:MovieDB/model/tv_list_model.dart'; 4 | import 'package:MovieDB/pages/tv_detail_page.dart'; 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | import 'package:firebase_auth/firebase_auth.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_bloc/flutter_bloc.dart'; 9 | import 'package:MovieDB/repository/constants.dart'; 10 | import 'package:MovieDB/widgets/custom_path_clipper.dart'; 11 | import 'package:shimmer/shimmer.dart'; 12 | 13 | class TvItemHorizontal extends StatelessWidget { 14 | const TvItemHorizontal({ 15 | Key key, 16 | @required this.tv, 17 | this.user, 18 | }) : super(key: key); 19 | 20 | final Result tv; 21 | final User user; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | MoviesBloc _movieBloc = MoviesBloc(); 26 | // print("${tv.id}"); 27 | // _moviesBloc.add(GetWatchListItemEvent(id: movie.id, uid: user?.uid)); 28 | return Container( 29 | margin: EdgeInsets.all(8.0), 30 | width: 100, 31 | child: InkWell( 32 | borderRadius: BorderRadius.circular(10), 33 | onTap: () { 34 | Navigator.of(context).push(MaterialPageRoute( 35 | builder: (BuildContext context) => TvDetailPage(id: tv.id))); 36 | }, 37 | child: Container( 38 | // margin: EdgeInsets.all(10.0), 39 | // height: 250, 40 | // width: 100, 41 | decoration: BoxDecoration( 42 | // color: Colors.white, 43 | borderRadius: BorderRadius.circular(10), 44 | ), 45 | child: Stack(children: [ 46 | Column( 47 | mainAxisAlignment: MainAxisAlignment.center, 48 | // crossAxisAlignment: CrossAxisAlignment.center, 49 | children: [ 50 | Expanded( 51 | flex: 5, 52 | child: ClipRRect( 53 | borderRadius: BorderRadius.circular(10), 54 | child: CachedNetworkImage( 55 | imageUrl: tv.posterPath != null 56 | ? "${IMAGE_URL + tv.posterPath}" 57 | : IMAGE_TEMP_URL, 58 | fit: BoxFit.cover, 59 | placeholder: (context, url) => Center( 60 | child: Shimmer.fromColors( 61 | baseColor: Colors.grey[700], 62 | highlightColor: Colors.grey[600], 63 | child: Container( 64 | color: Colors.grey, 65 | width: 100, 66 | height: MediaQuery.of(context).size.height, 67 | ), 68 | )), 69 | errorWidget: (context, url, error) => Icon(Icons.error), 70 | ), 71 | )), 72 | Expanded( 73 | flex: 3, 74 | child: Padding( 75 | padding: const EdgeInsets.symmetric(vertical: 8.0), 76 | child: Column( 77 | crossAxisAlignment: CrossAxisAlignment.start, 78 | children: [ 79 | Text( 80 | '${tv.name}', 81 | style: TextStyle(fontWeight: FontWeight.w500), 82 | maxLines: 2, 83 | overflow: TextOverflow.ellipsis, 84 | ), 85 | Row( 86 | children: [ 87 | Icon( 88 | Icons.star, 89 | color: Colors.orange, 90 | ), 91 | Text('${tv.voteAverage}') 92 | ], 93 | ) 94 | ], 95 | ), 96 | )), 97 | ], 98 | ), 99 | user != null 100 | ? BlocBuilder( 101 | bloc: _movieBloc, 102 | builder: (BuildContext context, MoviesState state) { 103 | if (state is WatchListItem) { 104 | return buildWatchListTag(state, _movieBloc, context); 105 | } 106 | return ClipPath( 107 | child: InkWell( 108 | onTap: () async { 109 | // MovieDetails movieDetails = 110 | // await new MovieRepository() 111 | // .getMovieDetails(tv.id); 112 | // _moviesBloc.add(AddWatchListEvent( 113 | // movieDetails: movieDetails, uid: user.uid)); 114 | }, 115 | child: Container( 116 | height: 50, 117 | width: 40, 118 | decoration: BoxDecoration( 119 | color: Colors.black.withOpacity(0.8), 120 | borderRadius: BorderRadius.only( 121 | topLeft: Radius.circular(10))), 122 | child: Center( 123 | child: Icon( 124 | Icons.add, 125 | color: Colors.white, 126 | ), 127 | ), 128 | ), 129 | ), 130 | clipper: CustomTagClipper()); 131 | }, 132 | ) 133 | : SizedBox.shrink() 134 | ]), 135 | ), 136 | ), 137 | ); 138 | } 139 | 140 | Widget buildWatchListTag( 141 | WatchListItem state, MoviesBloc moviesBloc, BuildContext context) { 142 | return Container( 143 | child: ClipPath( 144 | child: InkWell( 145 | onTap: () async { 146 | if (state.watchListItem != null) { 147 | // _moviesBloc.add( 148 | // DeleteWatchListMovieItem(uid: user.uid, movieId: movie.id)); 149 | } else { 150 | // MovieDetails movieDetails = 151 | // await new MovieRepository().getMovieDetails(movie.id); 152 | // _moviesBloc.add(AddWatchListEvent( 153 | // movieDetails: movieDetails, uid: user.uid)); 154 | } 155 | }, 156 | child: Container( 157 | height: 50, 158 | width: 40, 159 | decoration: BoxDecoration( 160 | color: state.watchListItem != null 161 | ? Theme.of(context).accentColor 162 | : Colors.black.withOpacity(0.8), 163 | borderRadius: 164 | BorderRadius.only(topLeft: Radius.circular(10))), 165 | child: Center( 166 | child: Icon( 167 | state.watchListItem != null ? Icons.check : Icons.add, 168 | color: 169 | state.watchListItem != null ? Colors.black : Colors.white, 170 | ), 171 | ), 172 | ), 173 | ), 174 | clipper: CustomTagClipper()), 175 | ); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /lib/widgets/tv_last_episode_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:MovieDB/model/tv_details.dart'; 2 | import 'package:MovieDB/pages/tv_seasons_page.dart'; 3 | import 'package:MovieDB/repository/constants.dart'; 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class TvLastEpisodeWidget extends StatelessWidget { 8 | const TvLastEpisodeWidget({ 9 | Key key, 10 | @required this.tv, 11 | @required this.screenSize, 12 | }) : super(key: key); 13 | 14 | final TvDetails tv; 15 | final Size screenSize; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Column( 20 | children: [ 21 | Row( 22 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 23 | children: [ 24 | Text( 25 | "Last Episode", 26 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), 27 | ), 28 | InkWell( 29 | borderRadius: BorderRadius.circular(16.0), 30 | onTap: () { 31 | Navigator.of(context).push(MaterialPageRoute( 32 | builder: (BuildContext context) => TvSeasonsList(tvDetails:tv))); 33 | }, 34 | child: Container( 35 | padding: EdgeInsets.symmetric(horizontal: 8, vertical: 5), 36 | decoration: BoxDecoration( 37 | color: Theme.of(context).accentColor.withOpacity(0.3), 38 | borderRadius: BorderRadius.circular(16.0)), 39 | child: Center( 40 | child: Text( 41 | "View All", 42 | style: TextStyle( 43 | fontWeight: FontWeight.w500, 44 | fontSize: 14, 45 | color: Theme.of(context).accentColor), 46 | ), 47 | ), 48 | ), 49 | ), 50 | ], 51 | ), 52 | SizedBox( 53 | height: 8, 54 | ), 55 | Card( 56 | elevation: 8, 57 | clipBehavior: Clip.antiAlias, 58 | shape: 59 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 60 | child: Stack( 61 | children: [ 62 | Container( 63 | width: screenSize.width, 64 | height: screenSize.width * 0.5, 65 | child: CachedNetworkImage( 66 | imageUrl: tv.posterPath != null 67 | ? "${IMAGE_URL + tv.lastEpisodeToAir.stillPath}" 68 | : IMAGE_TEMP_URL, 69 | fit: BoxFit.cover, 70 | placeholder: (context, url) => 71 | Center(child: CircularProgressIndicator()), 72 | errorWidget: (context, url, error) => Icon(Icons.error), 73 | ), 74 | ), 75 | Positioned( 76 | bottom: 0, 77 | child: Container( 78 | width: MediaQuery.of(context).size.width, 79 | color: Colors.black.withOpacity(0.7), 80 | padding: EdgeInsets.all(16.0), 81 | child: Column( 82 | crossAxisAlignment: CrossAxisAlignment.start, 83 | children: [ 84 | Text( 85 | '${tv.lastEpisodeToAir.name}', 86 | style: TextStyle( 87 | color: Colors.white, 88 | fontWeight: FontWeight.bold, 89 | fontSize: 16), 90 | ), 91 | SizedBox( 92 | height: 8, 93 | ), 94 | Text( 95 | 'Episode ${tv.lastEpisodeToAir.episodeNumber} Season ${tv.lastEpisodeToAir.seasonNumber}', 96 | style: TextStyle(color: Colors.white), 97 | ), 98 | ], 99 | )), 100 | ) 101 | ], 102 | ), 103 | ) 104 | ], 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:MovieDB/bloc/auth_bloc/auth_bloc.dart'; 4 | import 'package:MovieDB/bloc/auth_bloc/bloc.dart'; 5 | import 'package:MovieDB/pages/home.dart'; 6 | import 'package:MovieDB/pages/sign_in_page.dart'; 7 | 8 | class Wrapper extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | 12 | return BlocBuilder( 13 | bloc: BlocProvider.of(context), 14 | builder: (BuildContext context, AuthState state) { 15 | if(state is AuthLoginState){ 16 | var firebaseUser = state.user; 17 | if(firebaseUser!=null){ 18 | return Home(); 19 | }else{ 20 | return Home(); 21 | } 22 | } 23 | return SignInPage(); 24 | }, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: MovieDB 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | # google_fonts: ^0.3.3 23 | firebase_core : ^0.5.0 24 | firebase_auth: ^0.18.0+1 25 | cloud_firestore: ^0.14.0+2 26 | bloc: 27 | flutter_bloc: 28 | equatable: 29 | http: 30 | flutter_spinkit: ^4.1.2 31 | youtube_player_flutter: ^6.1.0+4 32 | cached_network_image: ^2.0.0 33 | image_downloader: ^0.19.1 34 | shared_preferences: ^0.5.6+2 35 | flutter_icons: ^1.0.0+1 36 | intl: ^0.16.1 37 | google_sign_in: ^4.1.4 38 | flutter_facebook_login: ^3.0.0 39 | esys_flutter_share: 40 | flutter_slidable: 41 | shimmer: ^1.1.0 42 | getwidget: 43 | # extended_text: ^0.7.1 44 | # provider: ^4.0.1 45 | 46 | 47 | 48 | # The following adds the Cupertino Icons font to your application. 49 | # Use with the CupertinoIcons class for iOS style icons. 50 | cupertino_icons: ^0.1.3 51 | 52 | dev_dependencies: 53 | flutter_test: 54 | sdk: flutter 55 | flutter_launcher_icons: 56 | rename: 57 | 58 | flutter_icons: 59 | android: true 60 | ios: true 61 | image_path: "assets/images/app_logo.png" 62 | 63 | 64 | # For information on the generic Dart part of this file, see the 65 | # following page: https://dart.dev/tools/pub/pubspec 66 | 67 | # The following section is specific to Flutter. 68 | flutter: 69 | 70 | # The following line ensures that the Material Icons font is 71 | # included with your application, so that you can use the icons in 72 | # the material Icons class. 73 | uses-material-design: true 74 | 75 | # To add assets to your application, add an assets section, like this: 76 | assets: 77 | - assets/images/ 78 | # - images/a_dot_ham.jpeg 79 | 80 | # An image asset can refer to one or more resolution-specific "variants", see 81 | # https://flutter.dev/assets-and-images/#resolution-aware. 82 | 83 | # For details regarding adding assets from package dependencies, see 84 | # https://flutter.dev/assets-and-images/#from-packages 85 | 86 | # To add custom fonts to your application, add a fonts section here, 87 | # in this "flutter" section. Each entry in this list should have a 88 | # "family" key with the font family name, and a "fonts" key with a 89 | # list giving the asset and other descriptors for the font. For 90 | # example: 91 | fonts: 92 | - family: Merriweather 93 | fonts: 94 | - asset: assets/fonts/Merriweather-Regular.ttf 95 | - asset: assets/fonts/Merriweather-Italic.ttf 96 | style: italic 97 | - family: Roboto 98 | fonts: 99 | - asset: assets/fonts/Roboto-Regular.ttf 100 | - asset: assets/fonts/Roboto-Bold.ttf 101 | weight: 700 102 | - family: Poppins-Bold 103 | fonts: 104 | - asset: assets/fonts/Poppins-Bold.ttf 105 | 106 | - family: Poppins-Medium 107 | fonts: 108 | - asset: assets/fonts/Poppins-Medium.ttf 109 | 110 | - family: Poppins-Light 111 | fonts: 112 | - asset: assets/fonts/Poppins-Light.otf 113 | 114 | - family: Poppins-ExtraLight 115 | fonts: 116 | - asset: assets/fonts/Poppins-ExtraLight.otf 117 | 118 | - family: Poppins-Regular 119 | fonts: 120 | - asset: assets/fonts/Poppins-Regular.otf 121 | 122 | - family: Poppins-SemiBold 123 | fonts: 124 | - asset: assets/fonts/Poppins-SemiBold.otf 125 | # For details regarding fonts from package dependencies, 126 | # see https://flutter.dev/custom-fonts/#from-packages 127 | -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060258.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060258.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060307.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060307.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060319.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060319.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060328.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060328.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060411.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060411.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060427.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060427.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060437.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060450.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060450.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060550.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060550.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060618.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060618.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060637.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060637.png -------------------------------------------------------------------------------- /screenshots/Screenshot_20200328-060646.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zubisofts/flutter-movie-db/41eb08398528290db76ead473e3d6c056b36fe40/screenshots/Screenshot_20200328-060646.png --------------------------------------------------------------------------------