├── android
├── settings_aar.gradle
├── app
│ ├── .settings
│ │ └── org.eclipse.buildship.core.prefs
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── movie_app_bloc_pattern
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── .classpath
│ ├── .project
│ └── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── settings.gradle
├── build.gradle
└── .project
├── ios
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Runner.xcodeproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
└── .gitignore
├── lib
├── src
│ ├── utils
│ │ ├── readme.md
│ │ ├── preferences.dart
│ │ └── auth.dart
│ ├── configs
│ │ ├── font_size_config.dart
│ │ ├── configs.dart
│ │ ├── color_config.dart
│ │ ├── size_config.dart
│ │ └── strings.dart
│ ├── views
│ │ ├── discover
│ │ │ └── discover_page.dart
│ │ ├── details
│ │ │ ├── components
│ │ │ │ ├── header_text.dart
│ │ │ │ ├── hide_show_btn.dart
│ │ │ │ ├── backdrop_card.dart
│ │ │ │ ├── genre_component.dart
│ │ │ │ ├── sliver_appbar_title.dart
│ │ │ │ ├── storyline_text.dart
│ │ │ │ ├── sliver_appbar_back_btn.dart
│ │ │ │ ├── poster_card.dart
│ │ │ │ └── bottom_tabbar.dart
│ │ │ ├── episode_details
│ │ │ │ └── episode_details_info
│ │ │ │ │ └── episode_details_info.dart
│ │ │ ├── season_details
│ │ │ │ ├── tabs
│ │ │ │ │ └── about
│ │ │ │ │ │ └── season_about_tab.dart
│ │ │ │ └── components
│ │ │ │ │ └── season_bottom_tabbar.dart
│ │ │ ├── tv_details
│ │ │ │ └── tabs
│ │ │ │ │ ├── tv_list
│ │ │ │ │ ├── tv_list.dart
│ │ │ │ │ ├── similar_list.dart
│ │ │ │ │ └── recommended_list.dart
│ │ │ │ │ ├── about
│ │ │ │ │ └── components
│ │ │ │ │ │ └── networks.dart
│ │ │ │ │ └── reviews
│ │ │ │ │ └── reviews_tab.dart
│ │ │ ├── movie_deatils
│ │ │ │ └── tabs
│ │ │ │ │ ├── movie _list
│ │ │ │ │ ├── movie_list.dart
│ │ │ │ │ ├── similar_list.dart
│ │ │ │ │ ├── collections_list.dart
│ │ │ │ │ └── recommended_list.dart
│ │ │ │ │ └── reviews
│ │ │ │ │ └── reviews_tab.dart
│ │ │ └── people_details
│ │ │ │ ├── tabs
│ │ │ │ └── about
│ │ │ │ │ └── components
│ │ │ │ │ ├── chips_builder.dart
│ │ │ │ │ └── about_info.dart
│ │ │ │ └── components
│ │ │ │ ├── people_flexible_spacebar_option.dart
│ │ │ │ └── people_bottom_tabbar.dart
│ │ ├── profile
│ │ │ ├── profile_page.dart
│ │ │ └── components
│ │ │ │ └── guest_credentials.dart
│ │ ├── home
│ │ │ ├── search
│ │ │ │ ├── components
│ │ │ │ │ ├── empty_search.dart
│ │ │ │ │ ├── search_history_list.dart
│ │ │ │ │ └── search_bottom_tabbar.dart
│ │ │ │ └── tabs
│ │ │ │ │ ├── movies_search_list.dart
│ │ │ │ │ ├── tv_search_list.dart
│ │ │ │ │ └── people_search_list.dart
│ │ │ └── components
│ │ │ │ └── header_tile.dart
│ │ ├── auth
│ │ │ └── components
│ │ │ │ ├── auth_btn.dart
│ │ │ │ └── webview_request_authorization.dart
│ │ └── watchlist
│ │ │ ├── components
│ │ │ └── watchlist_bottom_tabbar.dart
│ │ │ ├── tabs
│ │ │ ├── tv_watchlist.dart
│ │ │ └── movie_watchlist.dart
│ │ │ └── watchlist_page.dart
│ ├── controllers
│ │ ├── base_controller.dart
│ │ ├── test_bloc.dart
│ │ ├── download_controller.dart
│ │ ├── configuration_controller.dart
│ │ └── season_controller.dart
│ ├── mixins
│ │ ├── loading_spinner_mixin.dart
│ │ ├── query_parameter_mixin.dart
│ │ └── avatar.dart
│ ├── middlewares
│ │ └── auth_middleware.dart
│ ├── global
│ │ ├── more_btn.dart
│ │ ├── loading_spinner.dart
│ │ ├── form_wrapper.dart
│ │ ├── external_id_btn.dart
│ │ └── add_more_pagination_btn.dart
│ ├── models
│ │ ├── configurations
│ │ │ ├── language_configuration_model.dart
│ │ │ ├── country_configuration_model.dart
│ │ │ └── api_configuration_model.dart
│ │ ├── lists
│ │ │ ├── list_model.dart
│ │ │ └── movie_list_details.dart
│ │ ├── peoples
│ │ │ ├── people_external_ids.dart
│ │ │ ├── people_model.dart
│ │ │ ├── people_movie_credits.dart
│ │ │ └── people_tv_credits.dart
│ │ ├── account_model.dart
│ │ └── results
│ │ │ ├── movie_result_model.dart
│ │ │ └── tv_result_model.dart
│ ├── services
│ │ ├── auth_guest_service.dart
│ │ ├── configuration_service.dart
│ │ ├── search_service.dart
│ │ ├── download_service.dart
│ │ ├── trending_results_service.dart
│ │ ├── people_service.dart
│ │ └── results_service.dart
│ ├── helpers
│ │ └── widget_builder_helper.dart
│ ├── skeletons
│ │ ├── bottom_nav_skeleton.dart
│ │ ├── page_skeleton.dart
│ │ └── horizontal_bloc_skeleton.dart
│ ├── exceptions
│ │ └── app_exceptions.dart
│ ├── routes
│ │ └── route_const_str.dart
│ └── bindings
│ │ └── init_bindings.dart
├── service_locator.dart
├── main.dart
├── init_bindings.dart
└── youtube_iframe.html
├── web
├── favicon.png
├── icons
│ ├── Icon-192.png
│ └── Icon-512.png
└── manifest.json
├── fonts
├── Poppins-Black.ttf
├── Poppins-Bold.ttf
├── Poppins-Light.ttf
├── Poppins-Thin.ttf
├── Poppins-Italic.ttf
├── Poppins-Medium.ttf
├── Poppins-Regular.ttf
├── Poppins-BoldItalic.ttf
├── Poppins-ExtraBold.ttf
├── Poppins-ExtraLight.ttf
├── Poppins-SemiBold.ttf
├── Poppins-ThinItalic.ttf
├── Poppins-BlackItalic.ttf
├── Poppins-LightItalic.ttf
├── Poppins-MediumItalic.ttf
├── Poppins-ExtraBoldItalic.ttf
├── Poppins-SemiBoldItalic.ttf
└── Poppins-ExtraLightItalic.ttf
├── .metadata
├── .vscode
└── launch.json
├── .gitignore
├── pubspec.yaml
├── assets
├── facebook.svg
├── twitter.svg
├── imdb.svg
└── instagram.svg
├── .github
└── workflows
│ └── dart.yml
├── analysis_options.yaml
└── README.md
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/lib/src/utils/readme.md:
--------------------------------------------------------------------------------
1 | ### this directory is actually local storage for cache data
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/web/favicon.png
--------------------------------------------------------------------------------
/fonts/Poppins-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-Black.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-Light.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-Thin.ttf
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/fonts/Poppins-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-Italic.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-BoldItalic.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-ExtraBold.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-ExtraLight.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-ThinItalic.ttf
--------------------------------------------------------------------------------
/lib/src/configs/font_size_config.dart:
--------------------------------------------------------------------------------
1 | const double l = 24;
2 | const double m = 20;
3 | const double n = 16;
4 |
--------------------------------------------------------------------------------
/android/app/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=..
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/fonts/Poppins-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-BlackItalic.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-LightItalic.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-MediumItalic.ttf
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/fonts/Poppins-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/fonts/Poppins-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/fonts/Poppins-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/lib/src/configs/configs.dart:
--------------------------------------------------------------------------------
1 | export 'color_config.dart';
2 | export 'font_size_config.dart';
3 | export 'size_config.dart';
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/himmat12/movie_db_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/movie_app_bloc_pattern/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.movie_app
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/views/discover/discover_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class DiscoverPage extends StatelessWidget {
4 | const DiscoverPage({Key? key}) : super(key: key);
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return const Center(
9 | child: Text('Discover'),
10 | );
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.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: bac5a0db3b52f76201e1892a190cda70c876416f
8 | channel: master
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/lib/src/controllers/base_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | enum ViewState { idle, retrived, busy, error }
4 |
5 | class BaseController extends GetxController {
6 | final _state = ViewState.idle.obs;
7 |
8 | ViewState get state => _state.value;
9 |
10 | void setState(ViewState newState) {
11 | _state.value = newState;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/app/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/lib/src/mixins/loading_spinner_mixin.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_spinkit/flutter_spinkit.dart';
2 |
3 | import '../configs/configs.dart';
4 |
5 | mixin LoadingSpinnerMixin {
6 | final fadingCircleSpinner = const SpinKitFadingCircle(
7 | size: 14,
8 | color: primaryDarkBlue,
9 | );
10 | final horizontalLoading = const SpinKitThreeBounce(
11 | size: 14,
12 | color: primaryblue,
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/lib/src/configs/color_config.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | const Color primaryDarkBlue = Color(0xff211b3b);
4 | const Color secondaryDarkBlue = Color(0xff2d2747);
5 | const Color primaryblue = Color(0xff1b99cf);
6 | const Color primaryDark = Color(0xff262829);
7 | const Color primaryWhite = Color(0xfff7f7f7);
8 | const Color primaryDarkBlue05 = Color(0xff313038);
9 | const Color primaryblue05 = Color(0xff1fafed);
10 |
--------------------------------------------------------------------------------
/android/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | arguments=
2 | auto.sync=false
3 | build.scans.enabled=false
4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
5 | connection.project.dir=
6 | eclipse.preferences.version=1
7 | gradle.user.home=
8 | java.home=C\:/Program Files/Java/jdk-14.0.2
9 | jvm.arguments=
10 | offline.mode=false
11 | override.workspace.settings=true
12 | show.console.view=true
13 | show.executions.view=true
14 |
--------------------------------------------------------------------------------
/lib/src/middlewares/auth_middleware.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:get/get_navigation/src/routes/route_middleware.dart';
3 |
4 | import '../utils/auth.dart';
5 |
6 | class AuthMiddleware extends GetMiddleware {
7 | @override
8 | int? get priority => 1;
9 |
10 | @override
11 | RouteSettings? redirect(String? route) =>
12 | Auth().isLoggedIn ? const RouteSettings(name: "/dashboard") : null;
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/src/global/more_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class MoreBtn extends StatelessWidget {
4 | const MoreBtn({
5 | Key? key,
6 | this.onTap,
7 | this.title,
8 | }) : super(key: key);
9 |
10 | final String? title;
11 | final void Function()? onTap;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return GestureDetector(
16 | onTap: onTap ?? () {},
17 | child: Container(
18 | color: Colors.transparent,
19 | child: Text(title ?? 'More'),
20 | ),
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.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": "movie_app_bloc_pattern",
9 | "request": "launch",
10 | "type": "dart",
11 | "args": [
12 | "--enable-experiment=non-nullable",
13 | ],
14 | "flutterMode": "debug"
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/lib/src/configs/size_config.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SizeConfig {
4 | late MediaQueryData mediaQueryData;
5 | late double screenHeight;
6 | late double screenWidth;
7 | late double blockVerticalHeight;
8 | late double blockHorizontalWidth;
9 |
10 | void initSize(BuildContext context) {
11 | mediaQueryData = MediaQuery.of(context);
12 | screenHeight = mediaQueryData.size.height;
13 | screenWidth = mediaQueryData.size.width;
14 | blockVerticalHeight = screenHeight / 100;
15 | blockHorizontalWidth = screenWidth / 100;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/src/global/loading_spinner.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_spinkit/flutter_spinkit.dart';
2 |
3 | import '../configs/color_config.dart';
4 | import '../configs/configs.dart';
5 |
6 | class LoadingSpinner {
7 | SpinKitFadingCircle fadingCircleSpinner = const SpinKitFadingCircle(
8 | // size: 14,
9 | color: primaryDarkBlue,
10 | );
11 |
12 | SpinKitFadingCircle miniFadingCircleSpinner = const SpinKitFadingCircle(
13 | size: 22,
14 | color: primaryWhite,
15 | );
16 |
17 | SpinKitThreeBounce horizontalLoading = const SpinKitThreeBounce(
18 | size: 14,
19 | color: primaryblue,
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "movie_app",
3 | "short_name": "movie_app",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/lib/src/views/details/components/header_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../configs/configs.dart';
4 |
5 | class HeaderBuilder extends StatelessWidget {
6 | const HeaderBuilder({Key? key, this.headerText}) : super(key: key);
7 | final String? headerText;
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Text(
12 | headerText ?? 'headerText',
13 | maxLines: 2,
14 | overflow: TextOverflow.ellipsis,
15 | style: TextStyle(
16 | color: primaryDarkBlue.withOpacity(0.8),
17 | fontSize: m - 4,
18 | fontWeight: FontWeight.w700,
19 | ),
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.20'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/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/ephemeral/
22 | Flutter/app.flx
23 | Flutter/app.zip
24 | Flutter/flutter_assets/
25 | Flutter/flutter_export_environment.sh
26 | ServiceDefinitions.json
27 | Runner/GeneratedPluginRegistrant.*
28 |
29 | # Exceptions to above rules.
30 | !default.mode1v3
31 | !default.mode2v3
32 | !default.pbxuser
33 | !default.perspectivev3
34 |
--------------------------------------------------------------------------------
/lib/src/models/configurations/language_configuration_model.dart:
--------------------------------------------------------------------------------
1 | class LanguageConfigurationModel {
2 | LanguageConfigurationModel({
3 | required this.iso6391,
4 | required this.englishName,
5 | required this.name,
6 | });
7 |
8 | String iso6391;
9 | String englishName;
10 | String name;
11 |
12 | factory LanguageConfigurationModel.fromJson(Map json) =>
13 | LanguageConfigurationModel(
14 | iso6391: json["iso_639_1"],
15 | englishName: json["english_name"],
16 | name: json["name"],
17 | );
18 |
19 | Map toJson() => {
20 | "iso_639_1": iso6391,
21 | "english_name": englishName,
22 | "name": name,
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/views/profile/profile_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../utils/auth.dart';
4 | import 'components/guest_credentials.dart';
5 | import 'components/user_credentials.dart';
6 |
7 | class ProfilePage extends StatelessWidget {
8 | const ProfilePage({Key? key}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Padding(
13 | padding: const EdgeInsets.fromLTRB(16, 28, 16, 0),
14 | child: Column(
15 | children: [
16 | // poster & username
17 | Auth().isGuestLoggedIn
18 | ? const GuestUserCredientials()
19 | : const AuthUserCredentials(),
20 | ],
21 | ),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/models/configurations/country_configuration_model.dart:
--------------------------------------------------------------------------------
1 | class CountryConfigurationModel {
2 | CountryConfigurationModel({
3 | required this.iso31661,
4 | required this.englishName,
5 | required this.nativeName,
6 | });
7 |
8 | String iso31661;
9 | String englishName;
10 | String nativeName;
11 |
12 | factory CountryConfigurationModel.fromJson(Map json) =>
13 | CountryConfigurationModel(
14 | iso31661: json["iso_3166_1"],
15 | englishName: json["english_name"],
16 | nativeName: json["native_name"],
17 | );
18 |
19 | Map toJson() => {
20 | "iso_3166_1": iso31661,
21 | "english_name": englishName,
22 | "native_name": nativeName,
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/utils/preferences.dart:
--------------------------------------------------------------------------------
1 | import 'package:get_storage/get_storage.dart';
2 |
3 | final prefs = GetStorage('prefs');
4 |
5 | class Prefs {
6 | void setMoveiIstodayState(bool data) => prefs.write('movieIsToday', data);
7 | void setTvIstodayState(bool data) => prefs.write('tvIsToday', data);
8 |
9 | void setMovieNowPlayingState(bool data) =>
10 | prefs.write('isMovieNowPlaying', data);
11 | void setTvAiringTodayState(bool data) => prefs.write('isTvAiringToday', data);
12 |
13 | bool get isMovieNowPlayingState => prefs.read('isMovieNowPlaying') ?? true;
14 | bool get isTvAiringTodayState => prefs.read('isTvAiringToday') ?? true;
15 |
16 | bool get movieIsTodayState => prefs.read('movieIsToday') ?? true;
17 | bool get tvIsTodayState => prefs.read('tvIsToday') ?? true;
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/services/auth_guest_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import '../exceptions/app_exceptions.dart';
5 | import 'base_service.dart';
6 |
7 | class AuthGuestService extends BaseService {
8 | // create guest session
9 | Future createGuestSession() async {
10 | try {
11 | await request(
12 | method: Requests.get,
13 | path: '/3/authentication/guest_session/new',
14 | queryParameter: setQueryParameters(),
15 | );
16 |
17 | return decodeResponse(response);
18 | } on SocketException {
19 | throw FetchDataException('No Internet Connection');
20 | } on TimeoutException {
21 | throw ServiceNotRespondingException(
22 | 'Service not responding in time please check your Internet Connection');
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/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 |
19 | 1621751648172
20 |
21 | 30
22 |
23 | org.eclipse.core.resources.regexFilterMatcher
24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/lib/src/models/lists/list_model.dart:
--------------------------------------------------------------------------------
1 | class ListModel {
2 | ListModel({
3 | this.description,
4 | this.favoriteCount,
5 | this.id,
6 | this.itemCount,
7 | this.iso6391,
8 | this.listType,
9 | this.name,
10 | this.posterPath,
11 | });
12 |
13 | String? description;
14 | int? favoriteCount;
15 | int? id;
16 | int? itemCount;
17 | String? iso6391;
18 | String? listType;
19 | String? name;
20 | String? posterPath;
21 |
22 | factory ListModel.fromJson(Map json) => ListModel(
23 | description: json["description"],
24 | favoriteCount: json["favorite_count"],
25 | id: json["id"],
26 | itemCount: json["item_count"],
27 | iso6391: json["iso_639_1"],
28 | listType: json["list_type"],
29 | name: json["name"],
30 | posterPath: json["poster_path"],
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/src/mixins/query_parameter_mixin.dart:
--------------------------------------------------------------------------------
1 | import '../utils/auth.dart';
2 |
3 | mixin QueryParameterMixin {
4 | String api = "1a5ebef58b08ad825f24591860b26990";
5 | // String authorization =
6 | // "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxYTVlYmVmNThiMDhhZDgyNWYyNDU5MTg2MGIyNjk5MCIsInN1YiI6IjYwYTM1OTI2NzMxNGExMDA3OGZjZTRkOCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.VJG0GMDEpcYQBtm5VZlCHEmqTY5jH4kfIkYhosKqOA0";
7 |
8 | late Map queryParma;
9 |
10 | Map setQueryParameters({required Map query}) {
11 | queryParma = {"api_key": api};
12 | queryParma.addAll(query);
13 | if (Auth().isLoggedIn == true) {
14 | queryParma["session_id"] = Auth().sessionId;
15 | }
16 | if (Auth().isGuestLoggedIn == true) {
17 | queryParma["guest_session_id"] = Auth().guestSessionId;
18 | }
19 | return queryParma;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lib/src/services/configuration_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import '../exceptions/app_exceptions.dart';
5 | import 'base_service.dart';
6 |
7 | class ConfigurationService extends BaseService {
8 | Future getConfiguration() async {
9 | try {
10 | await request(
11 | method: Requests.get,
12 | path: "/3/configuration",
13 | header: setHeaders(),
14 | queryParameter: setQueryParameters());
15 | // ignore: avoid_print
16 | // print('CONFIGURATION STATUS => ${response.statusCode}');
17 |
18 | return decodeResponse(response);
19 | } on SocketException {
20 | throw FetchDataException('No Internet Connection');
21 | } on TimeoutException {
22 | throw ServiceNotRespondingException(
23 | 'Service not responding in time please check your Internet Connection');
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/src/services/search_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import '../exceptions/app_exceptions.dart';
5 | import 'base_service.dart';
6 |
7 | class SearchService extends BaseService {
8 | // search query
9 | Future search({
10 | required String query,
11 | required String resultType,
12 | }) async {
13 | try {
14 | await request(
15 | method: Requests.get,
16 | path: '/3/search/$resultType',
17 | queryParameter: setQueryParameters(query: {"query": query}));
18 | // print(decodeResponse(response)['results']);
19 |
20 | return decodeResponse(response)['results'];
21 | } on SocketException {
22 | throw FetchDataException('No Internet Connection');
23 | } on TimeoutException {
24 | throw ServiceNotRespondingException(
25 | 'Service not responding in time please check your Internet Connection');
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/views/home/search/components/empty_search.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../configs/configs.dart';
4 |
5 | class EmptySearch extends StatelessWidget {
6 | const EmptySearch({Key? key}) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Container(
11 | height: 200,
12 | alignment: Alignment.center,
13 | child: Column(
14 | mainAxisAlignment: MainAxisAlignment.center,
15 | children: [
16 | Icon(
17 | Icons.history,
18 | size: 48,
19 | color: primaryDarkBlue.withOpacity(0.6),
20 | ),
21 | Text(
22 | 'No search history yet',
23 | style: TextStyle(
24 | color: primaryDarkBlue.withOpacity(0.8),
25 | fontSize: n,
26 | ),
27 | ),
28 | ],
29 | ),
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 | .fvm/
34 |
35 | # Web related
36 | lib/generated_plugin_registrant.dart
37 |
38 | # Symbolication related
39 | app.*.symbols
40 |
41 | # Obfuscation related
42 | app.*.map.json
43 |
44 | # Android Studio will place build artifacts here
45 | /android/app/debug
46 | /android/app/profile
47 | /android/app/release
48 |
--------------------------------------------------------------------------------
/lib/src/helpers/widget_builder_helper.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../controllers/base_controller.dart';
4 |
5 | class WidgetBuilderHelper extends StatelessWidget {
6 | final ViewState state;
7 | final Widget onLoadingBuilder;
8 | final Widget onSuccessBuilder;
9 | final Widget onErrorBuilder;
10 |
11 | const WidgetBuilderHelper({
12 | required this.state,
13 | required this.onLoadingBuilder,
14 | required this.onSuccessBuilder,
15 | required this.onErrorBuilder,
16 | Key? key,
17 | }) : super(key: key);
18 | @override
19 | Widget build(BuildContext context) {
20 | switch (state) {
21 | case ViewState.busy:
22 | return onLoadingBuilder;
23 |
24 | case ViewState.error:
25 | return onErrorBuilder;
26 |
27 | case ViewState.retrived:
28 | break;
29 |
30 | default:
31 | break;
32 | }
33 | return onSuccessBuilder;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/src/controllers/test_bloc.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | enum EventAction { increment, decrement, reset }
4 |
5 | class TestBloc {
6 | late int count;
7 | final _testBlocStateController = StreamController();
8 | Stream get testStateStream =>
9 | _testBlocStateController.stream.asBroadcastStream();
10 |
11 | final _testBlocEventController = StreamController();
12 | StreamSink get testBlocEventSink =>
13 | _testBlocEventController.sink;
14 |
15 | TestBloc() {
16 | count = 0;
17 |
18 | Future.delayed(const Duration(seconds: 3));
19 |
20 | _testBlocEventController.stream.listen((event) {
21 | if (event == EventAction.increment) {
22 | count++;
23 | } else if (event == EventAction.decrement) {
24 | count--;
25 | } else if (event == EventAction.reset) {
26 | count = 0;
27 | }
28 |
29 | _testBlocStateController.sink.add(count);
30 | });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/src/skeletons/bottom_nav_skeleton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 | import 'package:shimmer/shimmer.dart';
4 |
5 | class BottomNavSkeleton extends StatelessWidget {
6 | const BottomNavSkeleton({Key? key}) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Shimmer.fromColors(
11 | baseColor: Colors.black26,
12 | highlightColor: Colors.blueGrey.shade50,
13 | child: Container(
14 | height: 60,
15 | margin: const EdgeInsets.fromLTRB(6, 6, 6, 6),
16 | child: Row(
17 | mainAxisAlignment: MainAxisAlignment.spaceAround,
18 | children: List.generate(
19 | 4,
20 | (index) => Container(
21 | height: 22,
22 | width: MediaQuery.of(Get.context!).size.width / 5,
23 | color: Colors.black26,
24 | ),
25 | ),
26 | ),
27 | ),
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/src/services/download_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import '../exceptions/app_exceptions.dart';
5 | import 'base_service.dart';
6 |
7 | // 'https://img.youtube.com/vi/gmRKv7n2If8/hqdefault.jpg' //default test img url
8 | class DownloadService extends BaseService {
9 | // download file
10 | Future downloadFile(
11 | {required String url, required String downloadPath}) async {
12 | try {
13 | final _response = await client.get(Uri.parse(url)).then((value) {
14 | File(downloadPath).writeAsBytes(value.bodyBytes).then((value) {
15 | print('DOWNLOAD COMPLETED: $value');
16 | });
17 | });
18 |
19 | return _response;
20 | } on SocketException {
21 | throw FetchDataException('No Internet Connection');
22 | } on TimeoutException {
23 | throw ServiceNotRespondingException(
24 | 'Service not responding in time please check your Internet Connection');
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/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 |
25 | 1621751648301
26 |
27 | 30
28 |
29 | org.eclipse.core.resources.regexFilterMatcher
30 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/lib/src/exceptions/app_exceptions.dart:
--------------------------------------------------------------------------------
1 | class AppException implements Exception {
2 | final String? _message;
3 | final String? _prefix;
4 |
5 | AppException(this._message, this._prefix);
6 |
7 | @override
8 | String toString() => '$_prefix$_message';
9 | }
10 |
11 | class FetchDataException extends AppException {
12 | FetchDataException([String? message])
13 | : super(message, 'Error During Communication: ');
14 | }
15 |
16 | class BadRequestException extends AppException {
17 | BadRequestException([String? message]) : super(message, 'Invalid Request: ');
18 | }
19 |
20 | class UnauthorizedException extends AppException {
21 | UnauthorizedException([String? message]) : super(message, 'Unauthorized: ');
22 | }
23 |
24 | class InvalidInputException extends AppException {
25 | InvalidInputException([String? message]) : super(message, 'Invalid Input: ');
26 | }
27 |
28 | class ServiceNotRespondingException extends AppException {
29 | ServiceNotRespondingException([String? message])
30 | : super(message, 'Not Responding: ');
31 | }
32 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/src/views/details/components/hide_show_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../configs/configs.dart';
5 | import '../../../controllers/utility_controller.dart';
6 |
7 | class ToggleHideShowBtn extends GetView {
8 | const ToggleHideShowBtn({Key? key}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Row(
13 | mainAxisAlignment: MainAxisAlignment.end,
14 | children: [
15 | GestureDetector(
16 | onTap: () {
17 | controller.toggleHideShowBtn();
18 | },
19 | child: Container(
20 | padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
21 | color: Colors.transparent,
22 | child: Obx(() => Text(
23 | controller.showText.value != true ? 'More' : 'Less',
24 | style: const TextStyle(
25 | color: primaryblue,
26 | fontSize: n - 2,
27 | ),
28 | )),
29 | ),
30 | ),
31 | ],
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/src/views/details/episode_details/episode_details_info/episode_details_info.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../controllers/season_controller.dart';
5 | import '../../components/storyline_text.dart';
6 | import '../episode_crew/episode_crew_builder.dart';
7 | import '../guest_casts/episode_guest_cast.dart';
8 |
9 | class EpisodeDetailsInfo extends StatelessWidget {
10 | EpisodeDetailsInfo({Key? key}) : super(key: key);
11 |
12 | final _seasonController = Get.find();
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Column(
17 | crossAxisAlignment: CrossAxisAlignment.start,
18 | children: [
19 | const SizedBox(height: 18),
20 | StoryLineTextBuilder(
21 | text: _seasonController.episodeModel.value.overview ?? "overview"),
22 | const SizedBox(height: 18),
23 | GuestCasts(),
24 | const SizedBox(height: 18),
25 | EpisodeCrewBuilder(
26 | crews: _seasonController.episodeModel.value.crew ?? []),
27 | const SizedBox(height: 18),
28 | ],
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/src/global/form_wrapper.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_form_builder/flutter_form_builder.dart';
3 |
4 | class FormWrapper extends StatelessWidget {
5 | const FormWrapper({
6 | required this.formKey,
7 | Key? key,
8 | this.autovalidateMode,
9 | this.initialValue,
10 | this.onChanged,
11 | this.skipDisabled,
12 | this.enabled,
13 | this.formFields,
14 | }) : super(key: key);
15 |
16 | final Key formKey;
17 | final AutovalidateMode? autovalidateMode;
18 | final Map? initialValue;
19 | final void Function()? onChanged;
20 | final bool? skipDisabled;
21 | final bool? enabled;
22 | final List? formFields;
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return FormBuilder(
27 | key: formKey,
28 | autovalidateMode: autovalidateMode ?? AutovalidateMode.onUserInteraction,
29 | initialValue: initialValue ?? {},
30 | onChanged: onChanged,
31 | skipDisabled: skipDisabled ?? false,
32 | enabled: enabled ?? true,
33 | child: Column(
34 | children: formFields ?? [],
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/src/global/external_id_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/svg.dart';
3 | import 'package:get/get.dart';
4 |
5 | import '../configs/configs.dart';
6 | import '../controllers/utility_controller.dart';
7 |
8 | class ExternalIdBtn extends StatelessWidget {
9 | ExternalIdBtn({
10 | required this.targetUrl,
11 | required this.asset,
12 | this.height = 22,
13 | this.width = 22,
14 | this.color,
15 | this.fit = BoxFit.scaleDown,
16 | Key? key,
17 | }) : super(key: key);
18 |
19 | final String targetUrl;
20 | final String asset;
21 | final double height;
22 | final double width;
23 | final BoxFit fit;
24 | final Color? color;
25 |
26 | final _utilityController = Get.find();
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | return GestureDetector(
31 | onTap: () {
32 | _utilityController.loadUrl(url: targetUrl);
33 | },
34 | child: SvgPicture.asset(
35 | 'assets/$asset',
36 | height: height,
37 | width: width,
38 | color: color ?? primaryDarkBlue.withOpacity(0.8),
39 | fit: fit,
40 | ),
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/src/services/trending_results_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import '../exceptions/app_exceptions.dart';
5 | import 'base_service.dart';
6 |
7 | class TrendingResultsService extends BaseService {
8 | // trending results service
9 | Future getTrendingResults({
10 | required String mediaType,
11 | required String timeWindow,
12 | String page = "",
13 | }) async {
14 | try {
15 | final response = await request(
16 | method: Requests.get,
17 | path: "/3/trending/$mediaType/$timeWindow",
18 | header: setHeaders(),
19 | queryParameter: setQueryParameters(query: {"page": page}),
20 | );
21 | // ignore: avoid_print
22 | // print('TRENDING $mediaType RESULTS STATUS => ${response.statusCode}');
23 | // ignore: avoid_print
24 | // print('PAGE => ${decodeResponse(response)['page']}');
25 |
26 | return decodeResponse(response)['results'];
27 | } on SocketException {
28 | throw FetchDataException('No Internet Connection');
29 | } on TimeoutException {
30 | throw ServiceNotRespondingException(
31 | 'Service not responding in time please check your Internet Connection');
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/src/views/details/components/backdrop_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:get/get.dart';
4 |
5 | import '../../../configs/configs.dart';
6 |
7 | class BackdropCard extends StatelessWidget {
8 | const BackdropCard({required this.imageUrl, Key? key}) : super(key: key);
9 |
10 | final String imageUrl;
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return GestureDetector(
15 | onTap: imageUrl.isEmpty
16 | ? null
17 | : () {
18 | Get.toNamed(
19 | '/backdrop_preview',
20 | arguments: {"filePath": imageUrl},
21 | );
22 | },
23 | child: CachedNetworkImage(
24 | fit: BoxFit.cover,
25 | imageUrl: imageUrl,
26 | errorWidget: (context, url, error) => Container(
27 | alignment: Alignment.center,
28 | // width: 94,
29 | // height: 140,
30 | color: Colors.black12,
31 | child: const Icon(
32 | Icons.error_outline,
33 | color: primaryWhite,
34 | size: 34,
35 | ),
36 | ),
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: movie_app
2 | description: A new Flutter project.
3 |
4 | publish_to: "none" # Remove this line if you wish to publish to pub.dev
5 |
6 | version: 1.0.0+1
7 |
8 | environment:
9 | sdk: ">=2.12.0 <3.0.0"
10 |
11 | dependency_overrides:
12 | platform: ^3.1.0
13 |
14 | dependencies:
15 | flutter:
16 | sdk: flutter
17 | cached_network_image: null
18 | cupertino_icons: ^1.0.2
19 | flutter_form_builder: ^6.0.1
20 | flutter_rating_bar: ^4.0.0
21 | flutter_spinkit: ^5.0.0
22 | flutter_svg: ^0.22.0
23 | get: ^4.1.4
24 | get_it: ^7.1.3
25 | get_storage: ^2.0.2
26 | http: ^0.13.3
27 | intl: ^0.17.0
28 | path_provider: ^2.0.2
29 | percent_indicator: ^3.3.0-nullsafety.1
30 | shimmer: ^2.0.0
31 | smooth_page_indicator: ^0.3.0-nullsafety.0
32 | url_launcher: ^6.0.6
33 | webview_flutter: ^2.0.9
34 | share_plus: ^2.1.4
35 |
36 | dev_dependencies:
37 | flutter_test:
38 | sdk: flutter
39 | build_runner: ^2.0.3
40 | flutter_lints: ^1.0.0
41 | json_serializable: ^4.1.3
42 |
43 | flutter:
44 | uses-material-design: true
45 |
46 | assets:
47 | - assets/
48 |
49 | fonts:
50 | - family: Poppins
51 | fonts:
52 | - asset: fonts/Poppins-Black.ttf
53 | - asset: fonts/Poppins-Medium.ttf
54 |
--------------------------------------------------------------------------------
/lib/src/models/lists/movie_list_details.dart:
--------------------------------------------------------------------------------
1 | import '../results/movie_result_model.dart';
2 |
3 | class MovieListDetails {
4 | MovieListDetails({
5 | this.createdBy,
6 | this.description,
7 | this.favoriteCount,
8 | this.id,
9 | this.items,
10 | this.itemCount,
11 | this.iso6391,
12 | this.name,
13 | this.posterPath,
14 | });
15 |
16 | String? createdBy;
17 | String? description;
18 | int? favoriteCount;
19 | String? id;
20 | List? items;
21 | int? itemCount;
22 | String? iso6391;
23 | String? name;
24 | String? posterPath;
25 |
26 | factory MovieListDetails.fromJson(Map json) =>
27 | MovieListDetails(
28 | createdBy: json["created_by"] as String,
29 | description: json["description"] as String,
30 | favoriteCount: json["favorite_count"] as int,
31 | id: json["id"] as String,
32 | items: json["items"] == null
33 | ? null
34 | : List.from((json["items"] as List)
35 | .map((e) => MovieResultModel.fromJson(e))),
36 | itemCount: json["item_count"] as int,
37 | iso6391: json["iso_639_1"] as String,
38 | name: json["name"] as String,
39 | posterPath: json["poster_path"] as String,
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/lib/src/views/auth/components/auth_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../configs/configs.dart';
4 |
5 | class AuthBtn extends StatelessWidget {
6 | const AuthBtn({
7 | Key? key,
8 | this.onTap,
9 | this.title,
10 | this.btnColor,
11 | }) : super(key: key);
12 |
13 | final void Function()? onTap;
14 | final String? title;
15 | final Color? btnColor;
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return GestureDetector(
20 | onTap: onTap,
21 | child: Container(
22 | // width: 120,
23 | margin: const EdgeInsets.symmetric(horizontal: 18),
24 | padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12),
25 | alignment: Alignment.center,
26 | decoration: BoxDecoration(
27 | color: btnColor ?? primaryblue,
28 | borderRadius: BorderRadius.circular(2),
29 | ),
30 | child: Text(
31 | // _authV3Controller.getSessionState == ViewState.busy
32 | // ? 'Authunticating ...'
33 | // :
34 | title ?? 'Sign In',
35 | style: const TextStyle(
36 | fontSize: m - 4,
37 | color: primaryWhite,
38 | ),
39 | ),
40 | ),
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/src/views/home/search/tabs/movies_search_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../controllers/search_controller.dart';
5 | import '../../../../global/loading_spinner.dart';
6 | import '../../../../helpers/widget_builder_helper.dart';
7 | import '../../../details/movie_deatils/tabs/movie%20_list/movie_list.dart';
8 |
9 | class MovieSearchList extends StatelessWidget {
10 | MovieSearchList({Key? key}) : super(key: key);
11 |
12 | final _searchController = Get.find();
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return GetBuilder(
17 | id: 'movie_search_result',
18 | init: _searchController,
19 | builder: (controller) => Obx(
20 | () => WidgetBuilderHelper(
21 | state: _searchController.searchState.value,
22 | onLoadingBuilder: Center(child: LoadingSpinner().fadingCircleSpinner),
23 | onErrorBuilder: const Center(
24 | child: Text('error while loading data ...'),
25 | ),
26 | onSuccessBuilder: Column(
27 | children: [
28 | MovieList(movies: _searchController.movieSearchResults),
29 | ],
30 | ),
31 | ),
32 | ),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/src/views/watchlist/components/watchlist_bottom_tabbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../details/components/bottom_tabbar.dart';
4 |
5 | class WatchlistTabbarComponent extends StatelessWidget {
6 | WatchlistTabbarComponent({required this.tabMenuItems, Key? key})
7 | : super(key: key);
8 | final List tabMenuItems;
9 |
10 | final _scrollController = ScrollController();
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return Container(
15 | height: kToolbarHeight,
16 | alignment: Alignment.centerLeft,
17 | child: SingleChildScrollView(
18 | controller: _scrollController,
19 | physics: const BouncingScrollPhysics(),
20 | padding: const EdgeInsets.symmetric(horizontal: 8),
21 | scrollDirection: Axis.horizontal,
22 | child: SizedBox(
23 | height: kToolbarHeight,
24 | child: Row(
25 | children: List.from(tabMenuItems.map((e) {
26 | int index = tabMenuItems.indexOf(e);
27 | return TabbarItem(
28 | index: index,
29 | title: e,
30 | controller: _scrollController,
31 | );
32 | })),
33 | ),
34 | ),
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/assets/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
42 |
--------------------------------------------------------------------------------
/lib/src/views/home/search/tabs/tv_search_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../controllers/search_controller.dart';
5 | import '../../../../global/loading_spinner.dart';
6 | import '../../../../helpers/widget_builder_helper.dart';
7 | import '../../../details/tv_details/tabs/tv_list/tv_list.dart';
8 |
9 | class TvSearchList extends StatelessWidget {
10 | TvSearchList({Key? key}) : super(key: key);
11 |
12 | final _searchController = Get.find();
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return GetBuilder(
17 | id: 'tv_search_result',
18 | init: _searchController,
19 | builder: (controller) => Obx(
20 | () => WidgetBuilderHelper(
21 | state: _searchController.searchState.value,
22 | onLoadingBuilder:
23 | Center(child: LoadingSpinner().fadingCircleSpinner),
24 | onErrorBuilder: const Center(
25 | child: Text('error while loading data ...'),
26 | ),
27 | onSuccessBuilder: Column(
28 | children: [
29 | TvList(tv: _searchController.tvSearchResults),
30 | ],
31 | ),
32 | ),
33 | ));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/src/views/watchlist/tabs/tv_watchlist.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../configs/strings.dart';
5 | import '../../../controllers/account_controller.dart';
6 | import '../../../global/loading_spinner.dart';
7 | import '../../../helpers/widget_builder_helper.dart';
8 | import '../../details/tv_details/tabs/tv_list/tv_list.dart';
9 |
10 | class TvWatchlist extends StatelessWidget {
11 | TvWatchlist({Key? key}) : super(key: key);
12 |
13 | final _accountController = Get.find();
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return GetBuilder(
18 | id: 'tv_watchlist',
19 | init: _accountController,
20 | initState: (_) {
21 | _accountController.getWatchlist(mediaType: tvString);
22 | },
23 | builder: (controller) => Obx(
24 | () => WidgetBuilderHelper(
25 | state: _accountController.watchlistState.value,
26 | onLoadingBuilder: Center(child: LoadingSpinner().fadingCircleSpinner),
27 | onErrorBuilder: const Center(
28 | child: Text('error while loading data ...'),
29 | ),
30 | onSuccessBuilder: Column(
31 | children: [
32 | TvList(tv: _accountController.tvWatchlist),
33 | ],
34 | ),
35 | ),
36 | ),
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/src/mixins/avatar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../configs/configs.dart';
4 |
5 | mixin AvatarBuilderMixin {
6 | Widget avatarBuilder({double? height, double? width}) => Container(
7 | height: height ?? 68,
8 | width: width ?? 68,
9 | color: primaryDarkBlue.withOpacity(0.4),
10 | child: Stack(
11 | alignment: AlignmentDirectional.bottomCenter,
12 | children: [
13 | Positioned(
14 | bottom: -26,
15 | child: Container(
16 | height: 62,
17 | width: 62,
18 | decoration: BoxDecoration(
19 | border: Border.all(color: primaryDarkBlue.withOpacity(0.4)),
20 | color: Colors.blueGrey.shade200,
21 | borderRadius: BorderRadius.circular(30),
22 | ),
23 | ),
24 | ),
25 | Positioned(
26 | bottom: 30,
27 | child: Container(
28 | height: 30,
29 | width: 30,
30 | decoration: BoxDecoration(
31 | border: Border.all(color: primaryDarkBlue.withOpacity(0.4)),
32 | color: Colors.blueGrey.shade200,
33 | borderRadius: BorderRadius.circular(30),
34 | ),
35 | ),
36 | ),
37 | ],
38 | ),
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/lib/src/models/peoples/people_external_ids.dart:
--------------------------------------------------------------------------------
1 | class PeopleExternelIds {
2 | PeopleExternelIds({
3 | this.id,
4 | this.freebaseMid,
5 | this.freebaseId,
6 | this.imdbId,
7 | this.tvrageId,
8 | this.facebookId,
9 | this.instagramId,
10 | this.twitterId,
11 | });
12 |
13 | int? id;
14 | String? freebaseMid;
15 | String? freebaseId;
16 | String? imdbId;
17 | int? tvrageId;
18 | String? facebookId;
19 | String? instagramId;
20 | String? twitterId;
21 |
22 | factory PeopleExternelIds.fromJson(Map json) =>
23 | _$PeopleExternelIdsFromJson(json);
24 | }
25 |
26 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
27 | ///
28 | ///
29 | ///
30 | /// generated code
31 | ///
32 | ///
33 | ///
34 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
35 |
36 | PeopleExternelIds _$PeopleExternelIdsFromJson(Map json) {
37 | return PeopleExternelIds(
38 | id: json['id'] as int?,
39 | freebaseMid: json['freebase_mid'] as String?,
40 | freebaseId: json['freebase_id'] as String?,
41 | imdbId: json['imdb_id'] as String?,
42 | tvrageId: json['tvrage_id'] as int?,
43 | facebookId: json['facebook_id'] as String?,
44 | instagramId: json['instagram_id'] as String?,
45 | twitterId: json['twitter_id'] as String?,
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/lib/src/routes/route_const_str.dart:
--------------------------------------------------------------------------------
1 | class RoutesPath {
2 | static const initialRoute = "/";
3 | static const splashScreenRoute = "/splash_screen";
4 | static const dashboardRoute = "/dashboard";
5 | static const movieDetailsRoute = "/movie_details";
6 | static const tvDetailsRoute = "/tv_details";
7 | static const peopleDetailsRoute = "/people_details";
8 | static const seasonDetailsRoute = "/season_details";
9 | static const episodeDetailsRoute = "/episode_details";
10 | static const episodeCrewRoute = "/episode_crew";
11 | static const tvCrewRoute = "/tv_crew";
12 | static const movieCrewRoute = "/movie_crew";
13 | static const guestStarsRoute = "/guest_stars";
14 | static const trendingMoviesListRoute = "/trending_movie_list";
15 | static const movieResultsListRoute = "/movie_results_list";
16 | static const trendingTvListRoute = "/trending_tv_list";
17 | static const tvResultsListRoute = "/tv_results_list";
18 | static const authorizationRoute = "/authorization";
19 | static const authRoute = "/auth";
20 | static const movieCollectionListRoute = "/movie_collection_list";
21 | static const posterViewerRoute = "/poster_viewer";
22 | static const backdropViewerRoute = "/backdrops_viewer";
23 | static const posterPreviewRoute = "/poster_preview";
24 | static const backdropPreviewRoute = "/backdrop_preview";
25 | static const searchRoute = "/search";
26 | static const profileRoute = "/profile_page";
27 | static const userProfileRoute = "/user_profile";
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/views/details/season_details/tabs/about/season_about_tab.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../controllers/details_controller.dart';
5 | import '../../../../../controllers/season_controller.dart';
6 | import '../../../components/genre_component.dart';
7 | import '../../../components/storyline_text.dart';
8 | import '../../../tv_details/tabs/about/components/networks.dart';
9 |
10 | class SeasonAboutTab extends StatelessWidget {
11 | final _detailsController = Get.find();
12 |
13 | final _seasonController = Get.find();
14 |
15 | SeasonAboutTab({Key? key}) : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Padding(
20 | padding: const EdgeInsets.symmetric(horizontal: 16),
21 | child: Column(
22 | crossAxisAlignment: CrossAxisAlignment.start,
23 | children: [
24 | const SizedBox(height: 18),
25 | StoryLineTextBuilder(
26 | text:
27 | _seasonController.seasonModel.value.overview ?? "storyline"),
28 | const SizedBox(height: 12),
29 | GenreBuilder(genres: _detailsController.tvDetail.value.genres ?? []),
30 | const SizedBox(height: 18),
31 | NetworkBuilder(
32 | networks: _detailsController.tvDetail.value.networks ?? []),
33 | const SizedBox(height: 18),
34 | ],
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/src/views/details/components/genre_component.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../configs/configs.dart';
4 | import '../../../models/details/common_details_models.dart';
5 | import 'header_text.dart';
6 |
7 | class GenreBuilder extends StatelessWidget {
8 | const GenreBuilder({required this.genres, Key? key}) : super(key: key);
9 | final List genres;
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Column(
14 | crossAxisAlignment: CrossAxisAlignment.start,
15 | children: [
16 | const HeaderBuilder(headerText: "Genre"),
17 | const SizedBox(height: 8),
18 | Wrap(
19 | spacing: 4,
20 | runSpacing: 6,
21 | runAlignment: WrapAlignment.start,
22 | children: List.from(
23 | genres.map(
24 | (e) => Container(
25 | padding:
26 | const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
27 | decoration: BoxDecoration(
28 | color: primaryDarkBlue.withOpacity(0.2),
29 | borderRadius: BorderRadius.circular(12),
30 | ),
31 | child: Text(
32 | e.name ?? "",
33 | style: TextStyle(
34 | fontSize: n - 2,
35 | color: primaryDarkBlue.withOpacity(0.7),
36 | ),
37 | ),
38 | ),
39 | ),
40 | ),
41 | ),
42 | ],
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/service_locator.dart:
--------------------------------------------------------------------------------
1 | import 'package:get_it/get_it.dart';
2 |
3 | import 'src/services/account_service.dart';
4 | import 'src/services/auth_v3_service.dart';
5 | import 'src/services/auth_v4_service.dart';
6 | import 'src/services/configuration_service.dart';
7 | import 'src/services/details_service.dart';
8 | import 'src/services/download_service.dart';
9 | import 'src/services/lists_services.dart';
10 | import 'src/services/people_service.dart';
11 | import 'src/services/results_service.dart';
12 | import 'src/services/search_service.dart';
13 | import 'src/services/season_service.dart';
14 | import 'src/services/trending_results_service.dart';
15 |
16 | final sl = GetIt.instance;
17 |
18 | void setUp() {
19 | sl.registerLazySingleton(() => ConfigurationService());
20 | sl.registerLazySingleton(() => ResultsService());
21 | sl.registerLazySingleton(
22 | () => TrendingResultsService());
23 | sl.registerLazySingleton(() => DetailsService());
24 | sl.registerLazySingleton(() => PeopleService());
25 | sl.registerLazySingleton(() => SeasonService());
26 | sl.registerLazySingleton(() => AuthV4Service());
27 | sl.registerLazySingleton(() => AuthV3Service());
28 | sl.registerLazySingleton(() => AccountService());
29 | sl.registerLazySingleton(() => ListService());
30 | sl.registerLazySingleton(() => DownloadService());
31 | sl.registerLazySingleton(() => SearchService());
32 | }
33 |
--------------------------------------------------------------------------------
/lib/src/views/home/components/header_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../configs/configs.dart';
4 |
5 | class HeaderTile extends StatelessWidget {
6 | const HeaderTile(
7 | {this.onMoreTap, this.subtitle, this.title, this.toggleOption, Key? key})
8 | : super(key: key);
9 |
10 | final String? title;
11 | final String? subtitle;
12 | final void Function()? onMoreTap;
13 | final Widget? toggleOption;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return Padding(
18 | padding: const EdgeInsets.symmetric(horizontal: 12),
19 | child: Row(
20 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
21 | children: [
22 | Row(
23 | children: [
24 | Text(
25 | title ?? 'title',
26 | style: const TextStyle(fontSize: m - 2),
27 | ),
28 | const SizedBox(width: 4),
29 | Text(
30 | subtitle ?? 'subtitle',
31 | style: const TextStyle(
32 | fontSize: m - 4,
33 | color: primaryblue,
34 | ),
35 | ),
36 | const SizedBox(width: 6),
37 | toggleOption ?? const SizedBox.shrink(),
38 | ],
39 | ),
40 | IconButton(
41 | onPressed: onMoreTap,
42 | icon: Icon(
43 | Icons.arrow_forward,
44 | color: primaryDarkBlue.withOpacity(0.8),
45 | )),
46 | ],
47 | ),
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/src/utils/auth.dart:
--------------------------------------------------------------------------------
1 | import 'package:get_storage/get_storage.dart';
2 |
3 | final box = GetStorage('auth');
4 |
5 | class Auth {
6 | bool get isLoggedIn => box.hasData('sessionId');
7 | bool get isGuestLoggedIn => box.hasData('guestSessionId');
8 |
9 | void setSessionId(String sessionId) async =>
10 | await box.write('sessionId', sessionId);
11 | void setGuestSessionId(String sessionId) async =>
12 | await box.write('guestSessionId', sessionId);
13 |
14 | void setUsername({String? usename}) async =>
15 | await box.write('username', usename);
16 | void setFullname({String? fullname}) async =>
17 | await box.write('fullname', fullname);
18 | void setUserAvatar({String? url}) async =>
19 | await box.write('user_avatar', url);
20 | void setUserGrvatar({String? url}) async =>
21 | await box.write('user_gravatar', url);
22 |
23 | void setSearchHistoryMovie(List history) async =>
24 | await box.write('history_movie', history);
25 |
26 | void setSearchHistoryTv(List history) async =>
27 | await box.write('history_tv', history);
28 |
29 | String get sessionId => box.read('sessionId');
30 | String get guestSessionId => box.read('guestSessionId');
31 |
32 | String? get username => box.read('username');
33 | String? get fullname => box.read('fullname');
34 | String? get userAvatar => box.read('user_avatar');
35 | String? get userGravatar => box.read('user_gravatar');
36 |
37 | List get searchHistoryMovie => box.read('history_movie') ?? [];
38 | List get searchHistoryTv => box.read('history_tv') ?? [];
39 |
40 | void logout() => box.erase();
41 | }
42 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:get/get.dart';
4 | import 'package:get_storage/get_storage.dart';
5 |
6 | import 'service_locator.dart';
7 | import 'splash_screen.dart';
8 | import 'src/bindings/init_bindings.dart';
9 | import 'src/configs/configs.dart';
10 | import 'src/routes/route_const_str.dart';
11 | import 'src/routes/routes.dart';
12 |
13 | void main() async {
14 | WidgetsFlutterBinding.ensureInitialized();
15 |
16 | // get storage initialization
17 | await GetStorage.init('auth');
18 | await GetStorage.init('prefs');
19 |
20 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
21 |
22 | // service locator initialization
23 | setUp();
24 |
25 | runApp(const MyApp(key: Key('muApp')));
26 | }
27 |
28 | class MyApp extends StatefulWidget {
29 | const MyApp({Key? key}) : super(key: key);
30 |
31 | @override
32 | State createState() => _MyAppState();
33 | }
34 |
35 | class _MyAppState extends State {
36 | @override
37 | Widget build(BuildContext context) {
38 | return GetMaterialApp(
39 | initialRoute: RoutesPath.splashScreenRoute,
40 | getPages: Routes.getRoutes(),
41 | initialBinding: InitBindings(),
42 | title: 'Movie DB',
43 | debugShowCheckedModeBanner: false,
44 | theme: ThemeData(
45 | fontFamily: 'Poppins',
46 | canvasColor: primaryWhite,
47 | // primaryColor: primaryWhite,
48 | ),
49 | // home: DashboardPage(),
50 | // home: const AuthPage(),
51 | home: const SplashScreen(),
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/init_bindings.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | import 'src/controllers/account_controller.dart';
4 | import 'src/controllers/auth_v3_controller.dart';
5 | import 'src/controllers/configuration_controller.dart';
6 | import 'src/controllers/details_controller.dart';
7 | import 'src/controllers/download_controller.dart';
8 | import 'src/controllers/list_controller.dart';
9 | import 'src/controllers/people_controller.dart';
10 | import 'src/controllers/results_controller.dart';
11 | import 'src/controllers/search_controller.dart';
12 | import 'src/controllers/season_controller.dart';
13 | import 'src/controllers/trending_results_controller.dart';
14 | import 'src/controllers/utility_controller.dart';
15 |
16 | class InitBindings extends Bindings {
17 | InitBindings() {
18 | dependencies();
19 | }
20 | @override
21 | void dependencies() {
22 | Get.lazyPut(() => ConfigurationController(), fenix: true);
23 | Get.lazyPut(() => UtilityController(), fenix: true);
24 | Get.lazyPut(() => ResultsController(), fenix: true);
25 | Get.lazyPut(() => TrendingResultsController(), fenix: true);
26 | Get.lazyPut(() => DetailsController(), fenix: true);
27 | Get.lazyPut(() => PeopleController(), fenix: true);
28 | Get.lazyPut(() => SeasonController(), fenix: true);
29 | Get.lazyPut(() => AuthV3Controller(), fenix: true);
30 | Get.lazyPut(() => AccountController(), fenix: true);
31 | Get.lazyPut(() => ListController(), fenix: true);
32 | Get.lazyPut(() => DownloadController(), fenix: true);
33 | Get.lazyPut(() => AccountController(), fenix: true);
34 | Get.put(SearchController(), permanent: true);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/src/bindings/init_bindings.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | import '../controllers/account_controller.dart';
4 | import '../controllers/auth_v3_controller.dart';
5 | import '../controllers/configuration_controller.dart';
6 | import '../controllers/details_controller.dart';
7 | import '../controllers/download_controller.dart';
8 | import '../controllers/list_controller.dart';
9 | import '../controllers/people_controller.dart';
10 | import '../controllers/results_controller.dart';
11 | import '../controllers/search_controller.dart';
12 | import '../controllers/season_controller.dart';
13 | import '../controllers/trending_results_controller.dart';
14 | import '../controllers/utility_controller.dart';
15 |
16 | class InitBindings extends Bindings {
17 | InitBindings() {
18 | dependencies();
19 | }
20 | @override
21 | void dependencies() {
22 | Get.lazyPut(() => ConfigurationController(), fenix: true);
23 | Get.lazyPut(() => UtilityController(), fenix: true);
24 | Get.lazyPut(() => ResultsController(), fenix: true);
25 | Get.lazyPut(() => TrendingResultsController(), fenix: true);
26 | Get.lazyPut(() => DetailsController(), fenix: true);
27 | Get.lazyPut(() => PeopleController(), fenix: true);
28 | Get.lazyPut(() => SeasonController(), fenix: true);
29 | Get.lazyPut(() => AuthV3Controller(), fenix: true);
30 | Get.lazyPut(() => AccountController(), fenix: true);
31 | Get.lazyPut(() => ListController(), fenix: true);
32 | Get.lazyPut(() => DownloadController(), fenix: true);
33 | Get.lazyPut(() => AccountController(), fenix: true);
34 | Get.put(SearchController(), permanent: true);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/src/global/add_more_pagination_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../configs/configs.dart';
4 | import '../controllers/base_controller.dart';
5 | import 'loading_spinner.dart';
6 |
7 | class AddMorePaginationBtn extends StatelessWidget {
8 | const AddMorePaginationBtn({
9 | required this.onTap,
10 | required this.viewState,
11 | Key? key,
12 | }) : super(key: key);
13 |
14 | final void Function()? onTap;
15 | final ViewState viewState;
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return GestureDetector(
20 | onTap: onTap,
21 | child: Column(
22 | children: [
23 | Stack(
24 | alignment: AlignmentDirectional.center,
25 | children: [
26 | Container(
27 | width: 88,
28 | height: 140,
29 | alignment: Alignment.center,
30 | padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),
31 | decoration: BoxDecoration(
32 | color: Colors.black38,
33 | borderRadius: BorderRadius.circular(4),
34 | ),
35 | ),
36 | Center(
37 | child: viewState == ViewState.busy
38 | ? LoadingSpinner().fadingCircleSpinner
39 | : const Icon(
40 | Icons.add,
41 | size: 34,
42 | color: primaryWhite,
43 | ),
44 | )
45 | ],
46 | ),
47 | const Text("\n"), //for spacing to match with other cards
48 | ],
49 | ),
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/.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 | # Note: This workflow uses the latest stable version of the Dart SDK.
22 | # You can specify other versions if desired, see documentation here:
23 | # https://github.com/dart-lang/setup-dart/blob/main/README.md
24 | # - uses: dart-lang/setup-dart@v1
25 | - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603
26 |
27 | - name: Install dependencies
28 | run: flutter pub get
29 |
30 | # Uncomment this step to verify the use of 'dart format' on each commit.
31 | # - name: Verify formatting
32 | # run: dart format --output=none --set-exit-if-changed .
33 |
34 | # Consider passing '--fatal-infos' for slightly stricter analysis.
35 | - name: Analyze project source
36 | run: dart analyze
37 |
38 | # Your project will need to have tests in test/ and a dependency on
39 | # package:test for this step to succeed. Note that Flutter projects will
40 | # want to change this to 'flutter test'.
41 | # - name: Run tests
42 | # run: dart test
43 | - name: build apk release
44 | - run: flutter clean
45 | - run: flutter pub get
46 | - run: dart pub get
47 | - run: flutter build apk --split-per-abi
48 |
--------------------------------------------------------------------------------
/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 | movie_app
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | linter:
4 | rules:
5 | # error rules
6 | avoid_print: false # Uncomment to disable the `avoid_print` rule
7 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
8 | # always_use_package_imports: true
9 | avoid_empty_else: true
10 | # prefer_relative_imports: true
11 | # avoid_relative_lib_imports: true
12 | avoid_returning_null_for_future: true
13 | avoid_type_to_string: true
14 | avoid_web_libraries_in_flutter: true
15 | cancel_subscriptions: true
16 | empty_statements: true
17 | iterable_contains_unrelated_type: true
18 | list_remove_unrelated_type: true
19 | literal_only_boolean_expressions: true
20 | no_adjacent_strings_in_list: true
21 | no_logic_in_create_state: true
22 | throw_in_finally: true
23 | unrelated_type_equality_checks: true
24 |
25 | # style rules
26 | always_declare_return_types: true
27 | always_put_control_body_on_new_line: true
28 | always_put_required_named_parameters_first: true
29 | always_require_non_null_named_parameters: true
30 | avoid_annotating_with_dynamic: true
31 | # avoid_bool_literals_in_conditional_expressions: true
32 | avoid_catches_without_on_clauses: true
33 | avoid_catching_errors: true
34 | avoid_classes_with_only_static_members: true
35 | avoid_double_and_int_checks: true
36 | avoid_function_literals_in_foreach_calls: true
37 | await_only_futures: true
38 | constant_identifier_names: true
39 | file_names: true
40 | flutter_style_todos: true
41 | #
42 | # Additional information about this file can be found at
43 | # https://dart.dev/guides/language/analysis-options
44 |
--------------------------------------------------------------------------------
/lib/src/services/people_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import '../exceptions/app_exceptions.dart';
5 | import 'base_service.dart';
6 |
7 | class PeopleService extends BaseService {
8 | /// fetches people details
9 | Future getPeopleDetails({required int personId}) async {
10 | try {
11 | final response = await request(
12 | method: Requests.get,
13 | path: '/3/person/$personId',
14 | header: setHeaders(),
15 | queryParameter: setQueryParameters());
16 | // ignore: avoid_print
17 | // print(response.statusCode);
18 | return decodeResponse(response);
19 | } on SocketException {
20 | throw FetchDataException('No Internet Connection');
21 | } on TimeoutException {
22 | throw ServiceNotRespondingException(
23 | 'Service not responding in time please check your Internet Connection');
24 | }
25 | }
26 |
27 | /// fetches people Movie/tv credits , images , external ids
28 | Future getCreditsDetails(
29 | {required int personId, required String resultType}) async {
30 | try {
31 | final response = await request(
32 | method: Requests.get,
33 | path: '/3/person/$personId/$resultType',
34 | header: setHeaders(),
35 | queryParameter: setQueryParameters());
36 | // ignore: avoid_print
37 | // print(response.statusCode);
38 | return decodeResponse(response);
39 | } on SocketException {
40 | throw FetchDataException('No Internet Connection');
41 | } on TimeoutException {
42 | throw ServiceNotRespondingException(
43 | 'Service not responding in time please check your Internet Connection');
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/src/views/details/components/sliver_appbar_title.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | // title widget
4 | class SABT extends StatefulWidget {
5 | final Widget child;
6 |
7 | const SABT({
8 | required this.child,
9 | Key? key,
10 | }) : super(key: key);
11 |
12 | @override
13 | _SABTState createState() => _SABTState();
14 | }
15 |
16 | class _SABTState extends State {
17 | ScrollPosition? _position;
18 | bool? _visible;
19 |
20 | @override
21 | void dispose() {
22 | _removeListener();
23 | super.dispose();
24 | }
25 |
26 | @override
27 | void didChangeDependencies() {
28 | super.didChangeDependencies();
29 | _removeListener();
30 | _addListener();
31 | }
32 |
33 | void _addListener() {
34 | _position = Scrollable.of(context)?.position;
35 | _position?.addListener(_positionListener);
36 | _positionListener();
37 | }
38 |
39 | void _removeListener() {
40 | _position?.removeListener(_positionListener);
41 | }
42 |
43 | void _positionListener() {
44 | final FlexibleSpaceBarSettings? settings =
45 | context.dependOnInheritedWidgetOfExactType();
46 | bool visible =
47 | settings == null || settings.currentExtent <= settings.minExtent;
48 | if (_visible != visible) {
49 | setState(() {
50 | _visible = visible;
51 | });
52 | }
53 | }
54 |
55 | @override
56 | Widget build(BuildContext context) {
57 | return Visibility(
58 | visible: _visible!,
59 | child: AnimatedOpacity(
60 | duration: const Duration(milliseconds: 300),
61 | opacity: 1,
62 | curve: Curves.easeIn,
63 | child: widget.child,
64 | ),
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/src/controllers/download_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'dart:math';
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:get/get.dart';
6 | import 'package:path_provider/path_provider.dart';
7 |
8 | import '../../service_locator.dart';
9 | import '../configs/configs.dart';
10 | import '../services/download_service.dart';
11 | import 'base_controller.dart';
12 |
13 | class DownloadController extends BaseController {
14 | final _service = sl();
15 |
16 | var downloadState = ViewState.idle.obs;
17 | var filePath = ''.obs;
18 | var isExpanded = true.obs;
19 |
20 | Directory? dir;
21 |
22 | void setFileUrl(url) => filePath.value = url;
23 |
24 | void resetDownloadState() => downloadState.value = ViewState.idle;
25 |
26 | void toggleExpanded() => isExpanded.value = !isExpanded.value;
27 |
28 | // download image files
29 | void downloadFile({required String url}) async {
30 | downloadState.value = ViewState.busy;
31 | if (Platform.isAndroid) {
32 | dir = await getExternalStorageDirectory();
33 | print(dir!.path);
34 | }
35 | await _service.downloadFile(
36 | url: url, downloadPath: '${dir!.path}/${Random().hashCode}.jpg');
37 | Get.showSnackbar(GetBar(
38 | icon: const Icon(
39 | Icons.file_download_done,
40 | size: 26,
41 | color: primaryWhite,
42 | ),
43 | messageText: Text(
44 | "path: ${dir!.path}",
45 | maxLines: 2,
46 | overflow: TextOverflow.ellipsis,
47 | style: const TextStyle(
48 | fontSize: n - 4,
49 | color: primaryWhite,
50 | ),
51 | ),
52 | dismissDirection: SnackDismissDirection.HORIZONTAL,
53 | duration: const Duration(milliseconds: 1800),
54 | ));
55 |
56 | downloadState.value = ViewState.retrived;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > ### bugs free version available at _stable*_ branch
2 |
3 | # movie_app
4 | 
5 |
6 | >**"This app project (movie_app) uses the TMDb API but is not endorsed or certified by [TMDb](https://www.themoviedb.org/)."**
7 |
8 | >movie database app with TMDb api data (still in development)*
9 |
10 |
11 | ## screen shots of App
12 |
13 |  
14 |
15 |  
16 |
17 |  
18 |
19 |  
20 |
21 |  
22 |
23 |  
24 |
--------------------------------------------------------------------------------
/lib/src/views/details/tv_details/tabs/tv_list/tv_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../controllers/configuration_controller.dart';
5 | import '../../../../../global/tv_thumbnail_card.dart';
6 | import '../../../../../mixins/avatar.dart';
7 | import '../../../../../models/results/tv_result_model.dart';
8 |
9 | class TvList extends StatelessWidget with AvatarBuilderMixin {
10 | final List tv;
11 | final String? imageUrl;
12 | final void Function()? onLongPress;
13 |
14 | final _configurationController = Get.find();
15 |
16 | TvList({required this.tv, this.onLongPress, Key? key, this.imageUrl})
17 | : super(key: key);
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return Column(
22 | children: [
23 | const SizedBox(height: 28),
24 | GridView.builder(
25 | padding: const EdgeInsets.symmetric(horizontal: 14),
26 | shrinkWrap: true,
27 | physics: const NeverScrollableScrollPhysics(),
28 | itemCount: tv.length,
29 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
30 | crossAxisCount: 3,
31 | mainAxisSpacing: 12,
32 | crossAxisSpacing: 12,
33 | mainAxisExtent: 186,
34 | ),
35 | itemBuilder: (context, index) => AbsorbPointer(
36 | absorbing: tv[index].posterPath == null ? true : false,
37 | child: TvThumbnailCard(
38 | onLongPress: onLongPress,
39 | padding: const EdgeInsets.all(0),
40 | tv: tv[index],
41 | imageUrl:
42 | '${_configurationController.posterUrl}${tv[index].posterPath}',
43 | ),
44 | ),
45 | ),
46 | ],
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/src/views/details/movie_deatils/tabs/movie _list/movie_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../controllers/configuration_controller.dart';
5 | import '../../../../../global/movie_thumbnail_card.dart';
6 | import '../../../../../mixins/avatar.dart';
7 | import '../../../../../models/results/movie_result_model.dart';
8 |
9 | class MovieList extends StatelessWidget with AvatarBuilderMixin {
10 | final List movies;
11 | // final void Function()? onLongPress;
12 |
13 | final _configurationController = Get.find();
14 |
15 | MovieList({
16 | required this.movies,
17 | // this.onLongPress,
18 | Key? key,
19 | }) : super(key: key);
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return Column(
24 | children: [
25 | const SizedBox(height: 28),
26 | GridView.builder(
27 | padding: const EdgeInsets.symmetric(horizontal: 14),
28 | shrinkWrap: true,
29 | physics: const NeverScrollableScrollPhysics(),
30 | itemCount: movies.length,
31 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
32 | crossAxisCount: 3,
33 | mainAxisSpacing: 12,
34 | crossAxisSpacing: 12,
35 | mainAxisExtent: 186,
36 | ),
37 | itemBuilder: (context, index) => AbsorbPointer(
38 | absorbing: movies[index].posterPath == null ? true : false,
39 | child: MovieThumbnailCard(
40 | // onLongPress: onLongPress,
41 | padding: const EdgeInsets.all(0),
42 | movie: movies[index],
43 | imageUrl:
44 | '${_configurationController.posterUrl}${movies[index].posterPath}',
45 | ),
46 | ),
47 | ),
48 | ],
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/src/skeletons/page_skeleton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:shimmer/shimmer.dart';
3 |
4 | import 'horizontal_bloc_skeleton.dart';
5 |
6 | Widget pageSkeleton() {
7 | return SingleChildScrollView(
8 | child: Shimmer.fromColors(
9 | // period: const Duration(seconds: 2),
10 | baseColor: Colors.black26,
11 | highlightColor: Colors.blueGrey.shade50,
12 | child: Column(
13 | children: [
14 | SizedBox(
15 | height: 80,
16 | child: Row(
17 | mainAxisAlignment: MainAxisAlignment.spaceAround,
18 | children: [
19 | Container(
20 | height: 46,
21 | width: 46,
22 | decoration: const BoxDecoration(
23 | shape: BoxShape.circle,
24 | color: Colors.black26,
25 | ),
26 | ),
27 | Container(
28 | height: 22,
29 | width: 130,
30 | color: Colors.black26,
31 | ),
32 | Container(
33 | height: 30,
34 | width: 30,
35 | decoration: const BoxDecoration(
36 | // shape: BoxShape.circle,
37 | color: Colors.black26,
38 | ),
39 | ),
40 | ]),
41 | ),
42 | const HorizontalBlocSkeleton(),
43 | const SizedBox(height: 22),
44 | const HorizontalBlocSkeleton(),
45 | const SizedBox(height: 22),
46 | const HorizontalBlocSkeleton(),
47 | const SizedBox(height: 22),
48 | const HorizontalBlocSkeleton(),
49 | const SizedBox(height: 22),
50 | const HorizontalBlocSkeleton(),
51 | const SizedBox(height: 22),
52 | ],
53 | ),
54 | ),
55 | );
56 | }
57 |
--------------------------------------------------------------------------------
/lib/src/views/details/people_details/tabs/about/components/chips_builder.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../../../configs/configs.dart';
4 | import '../../../../components/header_text.dart';
5 |
6 | class ChipsBuilder extends StatelessWidget {
7 | const ChipsBuilder({required this.chips, Key? key}) : super(key: key);
8 | final List chips;
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Column(
13 | crossAxisAlignment: CrossAxisAlignment.start,
14 | children: [
15 | const HeaderBuilder(headerText: "Also known as"),
16 | const SizedBox(height: 8),
17 | chips.isEmpty
18 | ? Text(
19 | 'No data to show at the moment',
20 | style: TextStyle(
21 | color: primaryDarkBlue.withOpacity(0.6),
22 | fontSize: n - 2,
23 | ),
24 | )
25 | : Wrap(
26 | spacing: 4,
27 | runSpacing: 6,
28 | runAlignment: WrapAlignment.start,
29 | children: List.from(
30 | chips.map(
31 | (e) => Container(
32 | padding: const EdgeInsets.symmetric(
33 | horizontal: 10, vertical: 6),
34 | decoration: BoxDecoration(
35 | color: primaryDarkBlue.withOpacity(0.2),
36 | borderRadius: BorderRadius.circular(12),
37 | ),
38 | child: Text(
39 | e ?? "",
40 | style: TextStyle(
41 | fontSize: n - 2,
42 | color: primaryDarkBlue.withOpacity(0.7),
43 | ),
44 | ),
45 | ),
46 | ),
47 | ),
48 | ),
49 | ],
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/src/views/details/components/storyline_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../configs/configs.dart';
5 | import '../../../controllers/utility_controller.dart';
6 | import 'header_text.dart';
7 | import 'hide_show_btn.dart';
8 |
9 | class StoryLineTextBuilder extends GetView {
10 | const StoryLineTextBuilder({
11 | required this.text,
12 | this.maxLines,
13 | this.headerText,
14 | Key? key,
15 | }) : super(key: key);
16 |
17 | final String text;
18 | final int? maxLines;
19 | final String? headerText;
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return Column(
24 | crossAxisAlignment: CrossAxisAlignment.start,
25 | children: [
26 | HeaderBuilder(headerText: headerText ?? "Story Line"),
27 | const SizedBox(height: 8),
28 | text == ""
29 | ? Text(
30 | 'No data at the Moment',
31 | style: TextStyle(color: primaryDarkBlue.withOpacity(0.6)),
32 | )
33 | : Obx(
34 | () => controller.showText.value != true
35 | ? Text(
36 | text,
37 | maxLines: maxLines ?? 4,
38 | overflow: TextOverflow.ellipsis,
39 | style: TextStyle(
40 | color: primaryDarkBlue.withOpacity(0.6),
41 | fontSize: n - 2,
42 | ),
43 | )
44 | : Text(
45 | text,
46 | style: TextStyle(
47 | color: primaryDarkBlue.withOpacity(0.6),
48 | fontSize: n - 2,
49 | ),
50 | ),
51 | ),
52 | text == "" ? const SizedBox.shrink() : const ToggleHideShowBtn(),
53 | ],
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/src/views/details/components/sliver_appbar_back_btn.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../configs/color_config.dart';
5 |
6 | // title widget
7 | class SABTN extends StatefulWidget {
8 | final void Function()? onBack;
9 |
10 | const SABTN({Key? key, this.onBack}) : super(key: key);
11 |
12 | @override
13 | _SABTNState createState() => _SABTNState();
14 | }
15 |
16 | class _SABTNState extends State {
17 | ScrollPosition? _position;
18 | bool? _visible;
19 |
20 | @override
21 | void dispose() {
22 | _removeListener();
23 | super.dispose();
24 | }
25 |
26 | @override
27 | void didChangeDependencies() {
28 | super.didChangeDependencies();
29 | _removeListener();
30 | _addListener();
31 | }
32 |
33 | void _addListener() {
34 | _position = Scrollable.of(context)?.position;
35 | _position?.addListener(_positionListener);
36 | _positionListener();
37 | }
38 |
39 | void _removeListener() {
40 | _position?.removeListener(_positionListener);
41 | }
42 |
43 | void _positionListener() {
44 | final FlexibleSpaceBarSettings? settings =
45 | context.dependOnInheritedWidgetOfExactType();
46 | bool visible =
47 | settings == null || settings.currentExtent <= settings.minExtent;
48 | if (_visible != visible) {
49 | setState(() {
50 | _visible = visible;
51 | });
52 | }
53 | }
54 |
55 | @override
56 | Widget build(BuildContext context) {
57 | return AnimatedOpacity(
58 | duration: const Duration(milliseconds: 300),
59 | opacity: 1,
60 | curve: Curves.easeIn,
61 | child: IconButton(
62 | onPressed: widget.onBack ??
63 | () {
64 | Get.back();
65 | },
66 | icon: Icon(
67 | Icons.arrow_back,
68 | color: _visible == false ? primaryWhite : primaryDarkBlue,
69 | ),
70 | ),
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/lib/youtube_iframe.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/lib/src/views/profile/components/guest_credentials.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../configs/configs.dart';
5 | import '../../../controllers/auth_v3_controller.dart';
6 |
7 | class GuestUserCredientials extends GetView {
8 | const GuestUserCredientials({Key? key}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Column(
13 | crossAxisAlignment: CrossAxisAlignment.start,
14 | children: [
15 | Container(
16 | padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
17 | decoration: BoxDecoration(
18 | color: primaryDarkBlue,
19 | borderRadius: BorderRadius.circular(12),
20 | ),
21 | child: const Text(
22 | 'Guest*',
23 | style: TextStyle(
24 | fontSize: m + 2,
25 | fontWeight: FontWeight.w700,
26 | color: primaryblue,
27 | ),
28 | ),
29 | ),
30 | const SizedBox(height: 12),
31 |
32 | // signout
33 | GestureDetector(
34 | onTap: () {
35 | controller.logoutV3();
36 | },
37 | child: Container(
38 | color: Colors.transparent,
39 | padding: const EdgeInsets.symmetric(vertical: 8),
40 | child: Row(
41 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
42 | children: const [
43 | Text(
44 | 'Signout',
45 | style: TextStyle(
46 | color: primaryblue,
47 | fontSize: n,
48 | fontWeight: FontWeight.w600,
49 | ),
50 | ),
51 | Icon(
52 | Icons.arrow_forward_ios,
53 | size: 18,
54 | color: Colors.black54,
55 | ),
56 | ],
57 | ),
58 | ),
59 | ),
60 | ],
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/src/models/peoples/people_model.dart:
--------------------------------------------------------------------------------
1 | class PeopleModel {
2 | PeopleModel({
3 | this.adult,
4 | this.alsoKnownAs,
5 | this.biography,
6 | this.birthday,
7 | this.deathday,
8 | this.gender,
9 | this.homepage,
10 | this.id,
11 | this.imdbId,
12 | this.knownForDepartment,
13 | this.name,
14 | this.placeOfBirth,
15 | this.popularity,
16 | this.profilePath,
17 | });
18 |
19 | bool? adult;
20 | List? alsoKnownAs;
21 | String? biography;
22 | String? birthday;
23 | dynamic deathday;
24 | int? gender;
25 | dynamic homepage;
26 | int? id;
27 | String? imdbId;
28 | String? knownForDepartment;
29 | String? name;
30 | String? placeOfBirth;
31 | double? popularity;
32 | String? profilePath;
33 |
34 | factory PeopleModel.fromJson(Map json) =>
35 | _$PeopleModelFromJson(json);
36 | }
37 |
38 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
39 | ///
40 | ///
41 | ///
42 | /// generated code
43 | ///
44 | ///
45 | ///
46 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
47 |
48 | PeopleModel _$PeopleModelFromJson(Map json) {
49 | return PeopleModel(
50 | adult: json['adult'] as bool?,
51 | alsoKnownAs: (json['also_known_as'] as List?)
52 | ?.map((e) => e as String)
53 | .toList(),
54 | biography: json['biography'] as String?,
55 | birthday: json['birthday'] as String?,
56 | deathday: json['deathday'] as String?,
57 | gender: json['gender'] as int?,
58 | homepage: json['homepage'],
59 | id: json['id'] as int?,
60 | imdbId: json['imdb_id'] as String?,
61 | knownForDepartment: json['known_for_department'] as String?,
62 | name: json['name'] as String?,
63 | placeOfBirth: json['place_of_birth'] as String?,
64 | popularity: (json['popularity'] as num?)?.toDouble(),
65 | profilePath: json['profile_path'] as String?,
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/lib/src/services/results_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import '../exceptions/app_exceptions.dart';
5 | import 'base_service.dart';
6 |
7 | class ResultsService extends BaseService {
8 | // movie results service
9 | Future getMovieResults({
10 | required String resultType,
11 | String page = "",
12 | String region = "",
13 | }) async {
14 | try {
15 | final response = await request(
16 | method: Requests.get,
17 | path: "/3/movie/$resultType",
18 | header: setHeaders(),
19 | queryParameter: setQueryParameters(query: {"page": page}),
20 | );
21 | // ignore: avoid_print
22 | // print('$resultType MOVIE RESULTS STATUS => ${response.statusCode}');
23 | // ignore: avoid_print
24 | // print('PAGE => $page');
25 |
26 | return decodeResponse(response)['results'];
27 | } on SocketException {
28 | throw FetchDataException('No Internet Connection');
29 | } on TimeoutException {
30 | throw ServiceNotRespondingException(
31 | 'Service not responding in time please check your Internet Connection');
32 | }
33 | }
34 |
35 | // tv results service
36 | Future getTvResults({
37 | required String resultType,
38 | String page = "",
39 | }) async {
40 | try {
41 | final response = await request(
42 | method: Requests.get,
43 | path: "/3/tv/$resultType",
44 | header: setHeaders(),
45 | queryParameter: setQueryParameters(query: {"page": page}),
46 | );
47 | // ignore: avoid_print
48 | // print('$resultType TV RESULTS STATUS => ${response.statusCode}');
49 | // ignore: avoid_print
50 | // print('PAGE => ${decodeResponse(response)['page']}');
51 |
52 | return decodeResponse(response)['results'];
53 | } on SocketException {
54 | throw FetchDataException('No Internet Connection');
55 | } on TimeoutException {
56 | throw ServiceNotRespondingException(
57 | 'Service not responding in time please check your Internet Connection');
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/src/views/details/tv_details/tabs/tv_list/similar_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../configs/configs.dart';
5 | import '../../../../../configs/strings.dart';
6 | import '../../../../../controllers/details_controller.dart';
7 | import '../../../../../controllers/results_controller.dart';
8 | import '../../../../../global/loading_spinner.dart';
9 | import '../../../../../helpers/widget_builder_helper.dart';
10 | import 'tv_list.dart';
11 |
12 | class TvSimilarTab extends StatelessWidget {
13 | TvSimilarTab({Key? key}) : super(key: key);
14 |
15 | final _detailsController = Get.find();
16 | final _resultsController = Get.find();
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return GetBuilder(
21 | id: 'tv_similar',
22 | init: _detailsController,
23 | initState: (_) {
24 | _detailsController.getOtherDetails(
25 | resultType: tvString,
26 | id: _resultsController.tvId,
27 | appendTo: similarString);
28 | },
29 | builder: (controller) => WidgetBuilderHelper(
30 | state: _detailsController.similarState.value,
31 | onLoadingBuilder: LoadingSpinner().fadingCircleSpinner,
32 | onErrorBuilder: const Center(
33 | child: Text('error while loading data ...'),
34 | ),
35 | onSuccessBuilder: _detailsController.similarTv.value.results == null ||
36 | _detailsController.similarTv.value.results!.isEmpty
37 | ? SizedBox(
38 | height: 80,
39 | child: Center(
40 | child: Text(
41 | 'No Similar Movies at the Moment',
42 | style: TextStyle(color: primaryDarkBlue.withOpacity(0.6)),
43 | ),
44 | ),
45 | )
46 | : Column(
47 | children: [
48 | TvList(tv: _detailsController.similarTv.value.results ?? []),
49 | ],
50 | ),
51 | ),
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/src/skeletons/horizontal_bloc_skeleton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class HorizontalBlocSkeleton extends StatelessWidget {
4 | const HorizontalBlocSkeleton({Key? key}) : super(key: key);
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return Container(
9 | margin: const EdgeInsets.fromLTRB(6, 6, 6, 6),
10 | child: Column(
11 | crossAxisAlignment: CrossAxisAlignment.start,
12 | children: [
13 | Row(
14 | children: [
15 | Container(
16 | height: 20,
17 | width: 120,
18 | color: Colors.black26,
19 | ),
20 | const SizedBox(width: 12),
21 | Container(
22 | height: 20,
23 | width: 60,
24 | color: Colors.black26,
25 | ),
26 | ],
27 | ),
28 | const SizedBox(height: 12),
29 | SingleChildScrollView(
30 | physics: const NeverScrollableScrollPhysics(),
31 | scrollDirection: Axis.horizontal,
32 | child: Row(
33 | children: List.generate(
34 | 5,
35 | (index) => Column(
36 | crossAxisAlignment: CrossAxisAlignment.start,
37 | children: [
38 | Container(
39 | margin: const EdgeInsets.fromLTRB(0, 0, 6, 0),
40 | decoration: BoxDecoration(
41 | color: Colors.black26,
42 | borderRadius: BorderRadius.circular(4),
43 | ),
44 | width: 88,
45 | height: 140,
46 | ),
47 | const SizedBox(height: 8),
48 | Container(
49 | height: 20,
50 | width: 66,
51 | color: Colors.black26,
52 | ),
53 | ],
54 | ),
55 | ),
56 | ),
57 | ),
58 | ],
59 | ),
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/src/views/auth/components/webview_request_authorization.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// this webview is integrated for TMDb v4 API
3 | ///
4 | ///
5 | ///
6 | ///
7 | ///
8 | ///
9 | ///
10 | ///
11 | ///
12 | ///
13 | ///
14 | ///
15 | ///
16 | import 'package:flutter/material.dart';
17 | import 'package:get/get.dart';
18 | import 'package:movie_app/service_locator.dart';
19 | import 'package:movie_app/src/services/auth_v4_service.dart';
20 | import 'package:webview_flutter/webview_flutter.dart';
21 |
22 | class AuthorizeRequestToken extends StatefulWidget {
23 | const AuthorizeRequestToken({Key? key}) : super(key: key);
24 |
25 | @override
26 | _AuthorizeRequestTokenState createState() => _AuthorizeRequestTokenState();
27 | }
28 |
29 | class _AuthorizeRequestTokenState extends State {
30 | final service = sl();
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | return SafeArea(
35 | child: WebView(
36 | initialUrl: Get.parameters['url'],
37 | javascriptMode: JavascriptMode.unrestricted,
38 | onPageFinished: (url) async {
39 | if (url == "https://www.themoviedb.org/auth/access/approve") {
40 | Get.dialog(
41 | AlertDialog(
42 | actions: [
43 | TextButton(
44 | onPressed: () {
45 | service
46 | .createV4AccessToken(
47 | requestToken:
48 | Get.parameters['requestToken'] ?? "")
49 | .then((value) {
50 | Get.back();
51 | Get.back();
52 | });
53 | },
54 | child: const Text("Continue"),
55 | ),
56 | ],
57 | title: const Text('Authorized'),
58 | content: const Text(
59 | '3rd party Authuntication request has been authorized!!'),
60 | ),
61 | barrierDismissible: false,
62 | );
63 | }
64 | },
65 | ),
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/assets/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
49 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 31
30 |
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 |
36 | kotlinOptions {
37 | jvmTarget = '1.8'
38 | }
39 |
40 | sourceSets {
41 | main.java.srcDirs += 'src/main/kotlin'
42 | }
43 |
44 | defaultConfig {
45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
46 | applicationId "com.example.movie_app"
47 | minSdkVersion 19
48 | targetSdkVersion 31
49 | versionCode flutterVersionCode.toInteger()
50 | versionName flutterVersionName
51 | }
52 |
53 | buildTypes {
54 | release {
55 | // TODO: Add your own signing config for the release build.
56 | // Signing with the debug keys for now, so `flutter run --release` works.
57 | signingConfig signingConfigs.debug
58 | }
59 | }
60 | }
61 |
62 | flutter {
63 | source '../..'
64 | }
65 |
66 | dependencies {
67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
68 | }
69 |
--------------------------------------------------------------------------------
/lib/src/models/account_model.dart:
--------------------------------------------------------------------------------
1 | class AccountModel {
2 | AccountModel({
3 | this.avatar,
4 | this.id,
5 | this.iso6391,
6 | this.iso31661,
7 | this.name,
8 | this.includeAdult,
9 | this.username,
10 | });
11 |
12 | Avatar? avatar;
13 | int? id;
14 | String? iso6391;
15 | String? iso31661;
16 | String? name;
17 | bool? includeAdult;
18 | String? username;
19 |
20 | factory AccountModel.fromJson(Map json) => AccountModel(
21 | avatar: Avatar.fromJson(json["avatar"]),
22 | id: json["id"],
23 | iso6391: json["iso_639_1"],
24 | iso31661: json["iso_3166_1"],
25 | name: json["name"],
26 | includeAdult: json["include_adult"],
27 | username: json["username"],
28 | );
29 |
30 | Map toJson() => {
31 | "avatar": avatar!.toJson(),
32 | "id": id,
33 | "iso_639_1": iso6391,
34 | "iso_3166_1": iso31661,
35 | "name": name,
36 | "include_adult": includeAdult,
37 | "username": username,
38 | };
39 | }
40 |
41 | class Avatar {
42 | Avatar({
43 | this.gravatar,
44 | this.tmdb,
45 | });
46 |
47 | Gravatar? gravatar;
48 | Tmdb? tmdb;
49 |
50 | factory Avatar.fromJson(Map json) => Avatar(
51 | gravatar: Gravatar.fromJson(json["gravatar"]),
52 | tmdb: Tmdb.fromJson(json["tmdb"]),
53 | );
54 |
55 | Map toJson() => {
56 | "gravatar": gravatar!.toJson(),
57 | "tmdb": tmdb!.toJson(),
58 | };
59 | }
60 |
61 | class Gravatar {
62 | Gravatar({
63 | this.hash,
64 | });
65 |
66 | String? hash;
67 |
68 | factory Gravatar.fromJson(Map json) => Gravatar(
69 | hash: json["hash"],
70 | );
71 |
72 | Map toJson() => {
73 | "hash": hash,
74 | };
75 | }
76 |
77 | class Tmdb {
78 | Tmdb({
79 | this.avatarPath,
80 | });
81 |
82 | String? avatarPath;
83 |
84 | factory Tmdb.fromJson(Map json) => Tmdb(
85 | avatarPath: json["avatar_path"],
86 | );
87 |
88 | Map toJson() => {
89 | "avatar_path": avatarPath,
90 | };
91 | }
92 |
--------------------------------------------------------------------------------
/lib/src/views/details/tv_details/tabs/tv_list/recommended_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../configs/configs.dart';
5 | import '../../../../../configs/strings.dart';
6 | import '../../../../../controllers/details_controller.dart';
7 | import '../../../../../controllers/results_controller.dart';
8 | import '../../../../../global/loading_spinner.dart';
9 | import '../../../../../helpers/widget_builder_helper.dart';
10 | import 'tv_list.dart';
11 |
12 | class TvRecommendedTab extends StatelessWidget {
13 | TvRecommendedTab({Key? key}) : super(key: key);
14 |
15 | final _detailsController = Get.find();
16 | final _resultsController = Get.find();
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return GetBuilder(
21 | id: 'tv_recommended',
22 | init: _detailsController,
23 | initState: (_) {
24 | _detailsController.getOtherDetails(
25 | resultType: tvString,
26 | id: _resultsController.tvId,
27 | appendTo: recommendedSTring);
28 | },
29 | builder: (controller) => WidgetBuilderHelper(
30 | state: _detailsController.recommendedState.value,
31 | onLoadingBuilder: LoadingSpinner().fadingCircleSpinner,
32 | onErrorBuilder: const Center(
33 | child: Text('error while loading data ...'),
34 | ),
35 | onSuccessBuilder: _detailsController.recommendedTv.value.results ==
36 | null ||
37 | _detailsController.recommendedTv.value.results!.isEmpty
38 | ? SizedBox(
39 | height: 80,
40 | child: Center(
41 | child: Text(
42 | 'No Recommended TV Series at the Moment',
43 | style: TextStyle(color: primaryDarkBlue.withOpacity(0.6)),
44 | ),
45 | ),
46 | )
47 | : Column(
48 | children: [
49 | TvList(
50 | tv: _detailsController.recommendedTv.value.results ?? []),
51 | ],
52 | ),
53 | ),
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/src/configs/strings.dart:
--------------------------------------------------------------------------------
1 | const String movieString = "movie";
2 | const String moviesString = "movies";
3 | const String tvString = "tv";
4 | const String allString = "all";
5 | const String peopleString = "people";
6 | const String personString = "person";
7 | const String dayString = "day";
8 | const String weekString = "week";
9 | const String popularString = "popular";
10 | const String topRatedString = "top_rated";
11 | const String onTheAirString = "on_the_air";
12 | const String airingTodayString = "airing_today";
13 | const String latestString = "latest";
14 | const String nowPlayingString = "now_playing";
15 | const String upcomingString = "upcoming";
16 | const String trendingString = "trending";
17 |
18 | const String imagesString = "images";
19 | const String videosString = "videos";
20 | const String similarString = "similar";
21 | const String recommendedSTring = "recommendations";
22 | const String accountStateString = "account_states";
23 | const String reviewsString = "reviews";
24 | const String creditsString = "credits";
25 | const String externalIdsString = "external_ids";
26 |
27 | const String movieCreditsString = "movie_credits";
28 | const String tvCreditsString = "tv_credits";
29 |
30 | const String facebookString = "https://www.facebook.com";
31 | const String instagramString = "https://www.instagram.com";
32 | const String twitterString = "https://twitter.com";
33 | const String imdbString = "https://www.imdb.com/name";
34 |
35 | // ratings strings
36 | const String rateQuestionString = "Do you like it ?"; // question
37 | const String terribleString = "Terrible"; //0.5 - 1.5
38 | const String poorString = "Poor"; //1.5> - 3
39 | const String notBadString = "Not Bad"; //3> - 4
40 | const String okayString = "It's Okay!"; // 4> - 5.5
41 | const String goodString = "Good"; // 5.5> - 6.5
42 | const String greatString = "Great"; //6.5> - 8
43 | const String wonderfulString = "Wonderful"; //8> - <=10
44 |
45 | // youtube urls
46 | const youtubeUrlString = "https://youtu.be";
47 | const youtubeThumbnailString = "https://img.youtube.com/vi";
48 | const maxQualityString = "/maxresdefault.jpg";
49 | const hqQualityString = "/hqdefault.jpg";
50 | const mqQualityString = "/mqdefault.jpg";
51 | const sdQualityString = "/sddefault.jpg";
52 |
--------------------------------------------------------------------------------
/lib/src/views/details/movie_deatils/tabs/movie _list/similar_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../configs/configs.dart';
5 | import '../../../../../configs/strings.dart';
6 | import '../../../../../controllers/details_controller.dart';
7 | import '../../../../../controllers/results_controller.dart';
8 | import '../../../../../global/loading_spinner.dart';
9 | import '../../../../../helpers/widget_builder_helper.dart';
10 | import '../movie%20_list/movie_list.dart';
11 |
12 | class MovieSimilarTab extends StatelessWidget {
13 | MovieSimilarTab({Key? key}) : super(key: key);
14 |
15 | final _detailsController = Get.find();
16 | final _resultsController = Get.find();
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return GetBuilder(
21 | id: 'movie_similar',
22 | init: _detailsController,
23 | initState: (_) {
24 | _detailsController.getOtherDetails(
25 | resultType: movieString,
26 | id: _resultsController.movieId,
27 | appendTo: similarString);
28 | },
29 | builder: (controller) => WidgetBuilderHelper(
30 | state: _detailsController.similarState.value,
31 | onLoadingBuilder: LoadingSpinner().fadingCircleSpinner,
32 | onErrorBuilder: const Center(
33 | child: Text('error while loading data ...'),
34 | ),
35 | onSuccessBuilder: _detailsController.similarMovie.value.results ==
36 | null ||
37 | _detailsController.similarMovie.value.results!.isEmpty
38 | ? SizedBox(
39 | height: 80,
40 | child: Center(
41 | child: Text(
42 | 'No Similar Movies at the Moment',
43 | style: TextStyle(color: primaryDarkBlue.withOpacity(0.6)),
44 | ),
45 | ),
46 | )
47 | : Column(
48 | children: [
49 | MovieList(
50 | movies:
51 | _detailsController.similarMovie.value.results ?? []),
52 | ],
53 | ),
54 | ),
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/src/models/configurations/api_configuration_model.dart:
--------------------------------------------------------------------------------
1 | class ApiConfigurationModel {
2 | ApiConfigurationModel({
3 | required this.images,
4 | required this.changeKeys,
5 | });
6 |
7 | Images images;
8 | List changeKeys;
9 |
10 | factory ApiConfigurationModel.fromJson(Map json) =>
11 | ApiConfigurationModel(
12 | images: Images.fromJson(json["images"]),
13 | changeKeys: List.from(json["change_keys"].map((x) => x)),
14 | );
15 |
16 | Map toJson() => {
17 | "images": images.toJson(),
18 | "change_keys": List.from(changeKeys.map((x) => x)),
19 | };
20 | }
21 |
22 | class Images {
23 | Images({
24 | required this.baseUrl,
25 | required this.secureBaseUrl,
26 | required this.backdropSizes,
27 | required this.logoSizes,
28 | required this.posterSizes,
29 | required this.profileSizes,
30 | required this.stillSizes,
31 | });
32 |
33 | String baseUrl;
34 | String secureBaseUrl;
35 | List backdropSizes;
36 | List logoSizes;
37 | List posterSizes;
38 | List profileSizes;
39 | List stillSizes;
40 |
41 | factory Images.fromJson(Map json) => Images(
42 | baseUrl: json["base_url"],
43 | secureBaseUrl: json["secure_base_url"],
44 | backdropSizes: List.from(json["backdrop_sizes"].map((x) => x)),
45 | logoSizes: List.from(json["logo_sizes"].map((x) => x)),
46 | posterSizes: List.from(json["poster_sizes"].map((x) => x)),
47 | profileSizes: List.from(json["profile_sizes"].map((x) => x)),
48 | stillSizes: List.from(json["still_sizes"].map((x) => x)),
49 | );
50 |
51 | Map toJson() => {
52 | "base_url": baseUrl,
53 | "secure_base_url": secureBaseUrl,
54 | "backdrop_sizes": List.from(backdropSizes.map((x) => x)),
55 | "logo_sizes": List.from(logoSizes.map((x) => x)),
56 | "poster_sizes": List.from(posterSizes.map((x) => x)),
57 | "profile_sizes": List.from(profileSizes.map((x) => x)),
58 | "still_sizes": List.from(stillSizes.map((x) => x)),
59 | };
60 | }
61 |
--------------------------------------------------------------------------------
/lib/src/views/details/components/poster_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:get/get.dart';
4 |
5 | import '../../../configs/configs.dart';
6 | import '../../../controllers/configuration_controller.dart';
7 |
8 | class PosterCard extends GetView {
9 | const PosterCard({required this.imageUrl, Key? key}) : super(key: key);
10 | final String? imageUrl;
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return GestureDetector(
15 | onTap: imageUrl == null
16 | ? null
17 | : () {
18 | Get.toNamed(
19 | '/poster_preview',
20 | arguments: {"filePath": imageUrl},
21 | );
22 | },
23 | child: Container(
24 | padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),
25 | child: ClipRRect(
26 | borderRadius: BorderRadius.circular(4),
27 | child: imageUrl == null
28 | ? Container(
29 | alignment: Alignment.center,
30 | width: 94,
31 | height: 140,
32 | color: Colors.black12,
33 | child: const Icon(
34 | Icons.error_outline,
35 | color: primaryWhite,
36 | size: 34,
37 | ),
38 | )
39 | : CachedNetworkImage(
40 | width: 94,
41 | height: 140,
42 | fit: BoxFit.fill,
43 | errorWidget: (context, url, error) => Container(
44 | alignment: Alignment.center,
45 | decoration: const BoxDecoration(
46 | color: Colors.black12,
47 | ),
48 | child: const Icon(
49 | Icons.error,
50 | color: primaryWhite,
51 | ),
52 | ),
53 | imageUrl: '${controller.posterUrl}$imageUrl',
54 | placeholder: (context, url) => Container(
55 | color: Colors.black12,
56 | ),
57 | ),
58 | ),
59 | ),
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/src/controllers/configuration_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | import '../../service_locator.dart';
4 | import '../models/configurations/api_configuration_model.dart';
5 | import '../services/configuration_service.dart';
6 | import 'base_controller.dart';
7 |
8 | class ConfigurationController extends BaseController {
9 | @override
10 | void onInit() {
11 | super.onInit();
12 | getConfigurations();
13 | }
14 |
15 | final _service = sl();
16 | var configuration = ApiConfigurationModel(
17 | changeKeys: [],
18 | images: Images(
19 | baseUrl: "",
20 | secureBaseUrl: "",
21 | backdropSizes: [],
22 | logoSizes: [],
23 | posterSizes: [],
24 | profileSizes: [],
25 | stillSizes: [],
26 | )).obs;
27 |
28 | var configState = ViewState.idle.obs;
29 |
30 | String _posterUrl = "";
31 | String _backDropUrl = "";
32 | String _profileUrl = "";
33 | String _logoUrl = "";
34 | String _stillUrl = "";
35 |
36 | String get posterUrl => _posterUrl;
37 | String get backDropUrl => _backDropUrl;
38 | String get profileUrl => _profileUrl;
39 | String get logoUrl => _logoUrl;
40 | String get stillUrl => _stillUrl;
41 |
42 | void getConfigurations() async {
43 | configState.value = ViewState.busy;
44 | await _service.getConfiguration().then((value) {
45 | // if (value != null) {
46 | configuration.value = ApiConfigurationModel.fromJson(value);
47 | _posterUrl =
48 | '${configuration.value.images.secureBaseUrl}${configuration.value.images.posterSizes[3]}';
49 | _backDropUrl =
50 | '${configuration.value.images.secureBaseUrl}${configuration.value.images.backdropSizes[1]}';
51 | _profileUrl =
52 | '${configuration.value.images.secureBaseUrl}${configuration.value.images.profileSizes[2]}';
53 | _logoUrl =
54 | '${configuration.value.images.secureBaseUrl}${configuration.value.images.logoSizes[3]}';
55 | _stillUrl =
56 | '${configuration.value.images.secureBaseUrl}${configuration.value.images.stillSizes[3]}';
57 | // ignore: avoid_print
58 | // print(configuration.value.changeKeys);
59 | configState.value = ViewState.retrived;
60 | // }
61 | });
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/src/views/details/movie_deatils/tabs/movie _list/collections_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../configs/configs.dart';
5 | import '../../../../../controllers/details_controller.dart';
6 | import '../../../../../global/loading_spinner.dart';
7 | import '../../../../../helpers/widget_builder_helper.dart';
8 | import '../movie%20_list/movie_list.dart';
9 |
10 | class MoviesCollectionList extends StatelessWidget {
11 | MoviesCollectionList({required this.collectionId, Key? key})
12 | : super(key: key);
13 |
14 | final String collectionId;
15 |
16 | final _detailsController = Get.find();
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return Scaffold(
21 | appBar: AppBar(
22 | elevation: 0.5,
23 | title: Text(
24 | _detailsController.movieDetail.value.belongsToCollection!.name ??
25 | "collection name"),
26 | ),
27 | body: GetBuilder(
28 | id: 'movie_collections',
29 | init: _detailsController,
30 | initState: (_) {
31 | _detailsController.getCollectionsDetails(collectionId: collectionId);
32 | },
33 | builder: (controller) => WidgetBuilderHelper(
34 | state: _detailsController.collectionsDetailsState.value,
35 | onLoadingBuilder: LoadingSpinner().fadingCircleSpinner,
36 | onErrorBuilder: const Center(
37 | child: Text('error while loading data ...'),
38 | ),
39 | onSuccessBuilder: _detailsController.movieCollections.isEmpty
40 | ? SizedBox(
41 | height: 80,
42 | child: Center(
43 | child: Text(
44 | 'No Movies at the Moment',
45 | style: TextStyle(color: primaryDarkBlue.withOpacity(0.6)),
46 | ),
47 | ),
48 | )
49 | : SingleChildScrollView(
50 | child: Column(
51 | children: [
52 | MovieList(movies: _detailsController.movieCollections),
53 | ],
54 | ),
55 | ),
56 | ),
57 | ),
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/src/views/details/movie_deatils/tabs/movie _list/recommended_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../configs/configs.dart';
5 | import '../../../../../configs/strings.dart';
6 | import '../../../../../controllers/details_controller.dart';
7 | import '../../../../../controllers/results_controller.dart';
8 | import '../../../../../global/loading_spinner.dart';
9 | import '../../../../../helpers/widget_builder_helper.dart';
10 | import '../movie%20_list/movie_list.dart';
11 |
12 | class MovieRecommendedTab extends StatelessWidget {
13 | MovieRecommendedTab({Key? key}) : super(key: key);
14 |
15 | final _detailsController = Get.find();
16 | final _resultsController = Get.find();
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return GetBuilder(
21 | id: 'movie_recommended',
22 | init: _detailsController,
23 | initState: (_) {
24 | _detailsController.getOtherDetails(
25 | resultType: movieString,
26 | id: _resultsController.movieId,
27 | appendTo: recommendedSTring);
28 | },
29 | builder: (controller) => WidgetBuilderHelper(
30 | state: _detailsController.recommendedState.value,
31 | onLoadingBuilder: LoadingSpinner().fadingCircleSpinner,
32 | onErrorBuilder: const Center(
33 | child: Text('error while loading data ...'),
34 | ),
35 | onSuccessBuilder: _detailsController.recommendedMovie.value.results ==
36 | null ||
37 | _detailsController.recommendedMovie.value.results!.isEmpty
38 | ? SizedBox(
39 | height: 80,
40 | child: Center(
41 | child: Text(
42 | 'No Recommended Movies at the Moment',
43 | style: TextStyle(color: primaryDarkBlue.withOpacity(0.6)),
44 | ),
45 | ),
46 | )
47 | : Column(
48 | children: [
49 | MovieList(
50 | movies:
51 | _detailsController.recommendedMovie.value.results ??
52 | []),
53 | ],
54 | ),
55 | ),
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
9 |
16 |
20 |
24 |
29 |
33 |
34 |
35 |
36 |
37 |
38 |
40 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/lib/src/models/results/movie_result_model.dart:
--------------------------------------------------------------------------------
1 | class MovieResultModel {
2 | MovieResultModel({
3 | this.adult,
4 | this.backdropPath,
5 | this.genreIds,
6 | this.id,
7 | this.originalLanguage,
8 | this.originalTitle,
9 | this.overview,
10 | this.popularity,
11 | this.posterPath,
12 | this.releaseDate,
13 | this.title,
14 | this.video,
15 | this.voteAverage,
16 | this.voteCount,
17 | });
18 |
19 | bool? adult;
20 | String? backdropPath;
21 | List? genreIds;
22 | int? id;
23 | String? originalLanguage;
24 | String? originalTitle;
25 | String? overview;
26 | double? popularity;
27 | String? posterPath;
28 | DateTime? releaseDate;
29 | String? title;
30 | bool? video;
31 | double? voteAverage;
32 | int? voteCount;
33 |
34 | factory MovieResultModel.fromJson(Map json) =>
35 | MovieResultModel(
36 | adult: json['adult'] as bool?,
37 | backdropPath: json['backdrop_path'] as String?,
38 | genreIds: (json['genre_ids'] as List?)
39 | ?.map((e) => e as int)
40 | .toList(),
41 | id: json['id'] as int?,
42 | originalLanguage: json['original_language'] as String?,
43 | originalTitle: json['original_title'] as String?,
44 | overview: json['overview'] as String?,
45 | popularity: (json['popularity'] as num?)?.toDouble(),
46 | posterPath: json['poster_path'] as String?,
47 | releaseDate: json['release_date'] == null || json['release_date'] == ""
48 | ? null
49 | : DateTime.parse(json['release_date'] as String),
50 | title: json['title'] as String?,
51 | video: json['video'] as bool?,
52 | voteAverage: (json['vote_average'] as num?)?.toDouble(),
53 | voteCount: json['vote_count'] as int?,
54 | );
55 |
56 | Map toJson() => {
57 | 'adult': adult,
58 | 'backdropPath': backdropPath,
59 | 'genreIds': genreIds,
60 | 'id': id,
61 | 'originalLanguage': originalLanguage,
62 | 'originalTitle': originalTitle,
63 | 'overview': overview,
64 | 'popularity': popularity,
65 | 'posterPath': posterPath,
66 | 'releaseDate': releaseDate?.toIso8601String(),
67 | 'title': title,
68 | 'video': video,
69 | 'voteAverage': voteAverage,
70 | 'voteCount': voteCount,
71 | };
72 | }
73 |
--------------------------------------------------------------------------------
/lib/src/models/results/tv_result_model.dart:
--------------------------------------------------------------------------------
1 | class TvResultsModel {
2 | TvResultsModel({
3 | this.backdropPath,
4 | this.firstAirDate,
5 | this.genreIds,
6 | this.id,
7 | this.name,
8 | this.originCountry,
9 | this.originalLanguage,
10 | this.originalName,
11 | this.overview,
12 | this.popularity,
13 | this.posterPath,
14 | this.voteAverage,
15 | this.voteCount,
16 | });
17 |
18 | String? backdropPath;
19 | DateTime? firstAirDate;
20 | List? genreIds;
21 | int? id;
22 | String? name;
23 | List? originCountry;
24 | String? originalLanguage;
25 | String? originalName;
26 | String? overview;
27 | double? popularity;
28 | String? posterPath;
29 | double? voteAverage;
30 | int? voteCount;
31 |
32 | factory TvResultsModel.fromJson(Map json) => TvResultsModel(
33 | backdropPath: json['backdrop_path'] as String?,
34 | firstAirDate:
35 | json['first_air_date'] == null || json['first_air_date'] == ""
36 | ? null
37 | : DateTime.parse(json['first_air_date'] as String),
38 | genreIds: (json['genre_ids'] as List?)
39 | ?.map((e) => e as int)
40 | .toList(),
41 | id: json['id'] as int?,
42 | name: json['name'] as String?,
43 | originCountry: (json['origin_country'] as List?)
44 | ?.map((e) => e as String)
45 | .toList(),
46 | originalLanguage: json['original_language'] as String?,
47 | originalName: json['original_name'] as String?,
48 | overview: json['overview'] as String?,
49 | popularity: (json['popularity'] as num?)?.toDouble(),
50 | posterPath: json['poster_path'] as String?,
51 | voteAverage: (json['vote_average'] as num?)?.toDouble(),
52 | voteCount: json['vote_count'] as int?,
53 | );
54 |
55 | Map toJson() => {
56 | 'backdropPath': backdropPath,
57 | 'firstAirDate': firstAirDate?.toIso8601String(),
58 | 'genreIds': genreIds,
59 | 'id': id,
60 | 'name': name,
61 | 'originCountry': originCountry,
62 | 'originalLanguage': originalLanguage,
63 | 'originalName': originalName,
64 | 'overview': overview,
65 | 'popularity': popularity,
66 | 'posterPath': posterPath,
67 | 'voteAverage': voteAverage,
68 | 'voteCount': voteCount,
69 | };
70 | }
71 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/assets/imdb.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
55 |
--------------------------------------------------------------------------------
/lib/src/views/details/tv_details/tabs/about/components/networks.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:get/get.dart';
4 |
5 | import '../../../../../../configs/color_config.dart';
6 | import '../../../../../../configs/configs.dart';
7 | import '../../../../../../controllers/configuration_controller.dart';
8 | import '../../../../../../models/details/tv_details_model.dart';
9 | import '../../../../components/header_text.dart';
10 |
11 | class NetworkBuilder extends GetView {
12 | const NetworkBuilder({required this.networks, Key? key}) : super(key: key);
13 | final List networks;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return Column(
18 | crossAxisAlignment: CrossAxisAlignment.start,
19 | children: [
20 | const HeaderBuilder(headerText: "Networks"),
21 | const SizedBox(height: 8),
22 | Wrap(
23 | spacing: 4,
24 | runSpacing: 6,
25 | runAlignment: WrapAlignment.start,
26 | children: List.from(
27 | networks.map(
28 | (e) => Container(
29 | margin: const EdgeInsets.only(right: 4),
30 | padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
31 | decoration: BoxDecoration(
32 | color: primaryDarkBlue.withOpacity(0.2),
33 | borderRadius: BorderRadius.circular(12),
34 | ),
35 | child: e.logoPath == null || e.logoPath == ""
36 | ? const SizedBox(
37 | height: 40,
38 | width: 40,
39 | )
40 | : CachedNetworkImage(
41 | height: 40,
42 | width: 40,
43 | fit: BoxFit.scaleDown,
44 | errorWidget: (context, url, error) => Container(
45 | alignment: Alignment.center,
46 | decoration: const BoxDecoration(
47 | color: Colors.black12,
48 | ),
49 | child: const Icon(
50 | Icons.error,
51 | color: primaryWhite,
52 | ),
53 | ),
54 | imageUrl: '${controller.logoUrl}${e.logoPath}'),
55 | ),
56 | ),
57 | ),
58 | ),
59 | ],
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/src/views/details/people_details/components/people_flexible_spacebar_option.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:percent_indicator/percent_indicator.dart';
3 |
4 | import '../../../../configs/configs.dart';
5 | import '../../../../models/peoples/people_model.dart';
6 |
7 | class PeopleFlexibleSpacebarOptions extends StatelessWidget {
8 | const PeopleFlexibleSpacebarOptions({required this.people, Key? key})
9 | : super(key: key);
10 | final PeopleModel people;
11 | @override
12 | Widget build(BuildContext context) {
13 | return Padding(
14 | padding: const EdgeInsets.symmetric(horizontal: 14),
15 | child: Row(
16 | children: [
17 | people.popularity == null
18 | ? const SizedBox.shrink()
19 | : CircularPercentIndicator(
20 | radius: 56,
21 | percent: (people.popularity ?? 0.0).toInt() / 100,
22 | curve: Curves.ease,
23 | animation: true,
24 | animationDuration: 800,
25 | progressColor: primaryblue,
26 | center: Text(
27 | '${((people.popularity ?? 0.0).toInt())}%',
28 | style: const TextStyle(
29 | color: primaryDarkBlue,
30 | fontWeight: FontWeight.w700,
31 | ),
32 | ),
33 | ),
34 | const SizedBox(width: 4),
35 | const Text(
36 | 'User\nScore',
37 | textAlign: TextAlign.center,
38 | style: TextStyle(
39 | fontSize: n - 2,
40 | fontWeight: FontWeight.w700,
41 | color: primaryDarkBlue,
42 | ),
43 | ),
44 | ],
45 | ),
46 | );
47 | }
48 | }
49 |
50 | // helpers
51 | class OptionBtn extends StatelessWidget {
52 | const OptionBtn({this.color, this.icon, this.onTap, Key? key})
53 | : super(key: key);
54 | final IconData? icon;
55 | final void Function()? onTap;
56 | final Color? color;
57 | @override
58 | Widget build(BuildContext context) {
59 | return GestureDetector(
60 | onTap: onTap ?? () {},
61 | child: Stack(
62 | alignment: AlignmentDirectional.center,
63 | children: [
64 | Container(
65 | height: 46,
66 | width: 46,
67 | decoration: BoxDecoration(
68 | color: primaryDarkBlue.withOpacity(0.9),
69 | shape: BoxShape.circle,
70 | ),
71 | ),
72 | Icon(
73 | icon ?? Icons.list,
74 | color: color ?? primaryWhite,
75 | ),
76 | ],
77 | ),
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/lib/src/views/details/components/bottom_tabbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../configs/configs.dart';
5 | import '../../../controllers/utility_controller.dart';
6 |
7 | PreferredSizeWidget? bottomTabbarComponent(
8 | {required List tabMenuItems}) {
9 | final _scrollController = ScrollController();
10 |
11 | return PreferredSize(
12 | preferredSize: Size(MediaQuery.of(Get.context!).size.width, kToolbarHeight),
13 | child: Container(
14 | alignment: Alignment.centerLeft,
15 | child: SingleChildScrollView(
16 | controller: _scrollController,
17 | physics: const BouncingScrollPhysics(),
18 | padding: const EdgeInsets.symmetric(horizontal: 8),
19 | scrollDirection: Axis.horizontal,
20 | child: SizedBox(
21 | height: kToolbarHeight,
22 | child: Row(
23 | children: List.from(tabMenuItems.map((e) {
24 | int index = tabMenuItems.indexOf(e);
25 | return TabbarItem(
26 | index: index,
27 | title: e,
28 | controller: _scrollController,
29 | );
30 | })),
31 | ),
32 | ),
33 | ),
34 | ),
35 | );
36 | }
37 |
38 | // tabbar item helper zwidget
39 | class TabbarItem extends StatelessWidget {
40 | const TabbarItem(
41 | {required this.index,
42 | required this.title,
43 | required this.controller,
44 | Key? key})
45 | : super(key: key);
46 |
47 | final int index;
48 | final String title;
49 | final ScrollController controller;
50 |
51 | @override
52 | Widget build(BuildContext context) {
53 | final _utilityController = Get.find();
54 |
55 | return GestureDetector(
56 | onTap: () {
57 | _utilityController.setTabbarIndex(index);
58 | },
59 | child: Container(
60 | height: 80,
61 | color: Colors.transparent,
62 | alignment: Alignment.center,
63 | padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
64 | child:
65 | //
66 | //Obx(() =>
67 | GetBuilder(
68 | id: 'bottomTabBar',
69 | init: _utilityController,
70 | builder: (controller) => Text(
71 | title,
72 | style: TextStyle(
73 | fontSize: m - 6,
74 | color: _utilityController.tabbarCurrentIndex == index
75 | ? primaryDarkBlue.withOpacity(0.8)
76 | : primaryDarkBlue.withOpacity(0.5),
77 | fontWeight: FontWeight.w700,
78 | ),
79 | ),
80 | ),
81 |
82 | // ),
83 | ),
84 | );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/lib/src/views/details/season_details/components/season_bottom_tabbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../configs/configs.dart';
5 | import '../../../../controllers/utility_controller.dart';
6 |
7 | PreferredSizeWidget? seasonBottomTabbarComponent(
8 | {required List tabMenuItems}) {
9 | final _scrollController = ScrollController();
10 |
11 | return PreferredSize(
12 | preferredSize: Size(MediaQuery.of(Get.context!).size.width, kToolbarHeight),
13 | child: Container(
14 | alignment: Alignment.centerLeft,
15 | child: SingleChildScrollView(
16 | controller: _scrollController,
17 | physics: const BouncingScrollPhysics(),
18 | padding: const EdgeInsets.symmetric(horizontal: 8),
19 | scrollDirection: Axis.horizontal,
20 | child: SizedBox(
21 | height: kToolbarHeight,
22 | child: Row(
23 | children: List.from(tabMenuItems.map((e) {
24 | int index = tabMenuItems.indexOf(e);
25 | return TabbarItem(
26 | index: index,
27 | title: e,
28 | controller: _scrollController,
29 | );
30 | })),
31 | ),
32 | ),
33 | ),
34 | ),
35 | );
36 | }
37 |
38 | // helper tabbar items
39 | class TabbarItem extends StatelessWidget {
40 | TabbarItem(
41 | {required this.index,
42 | required this.title,
43 | required this.controller,
44 | Key? key})
45 | : super(key: key);
46 | final int index;
47 | final String title;
48 | final ScrollController controller;
49 |
50 | final _utilityController = Get.find();
51 | @override
52 | Widget build(BuildContext context) {
53 | return GestureDetector(
54 | onTap: () {
55 | _utilityController.setSeasonTabbarIndex(index);
56 | },
57 | child: Container(
58 | height: 80,
59 | color: Colors.transparent,
60 | alignment: Alignment.center,
61 | padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
62 | child:
63 | //
64 | //Obx(() =>
65 | GetBuilder(
66 | id: 'seasonBottomTabBar',
67 | init: _utilityController,
68 | builder: (controller) => Text(
69 | title,
70 | style: TextStyle(
71 | fontSize: m - 6,
72 | color: _utilityController.seasonTabbarCurrentIndex == index
73 | ? primaryDarkBlue.withOpacity(0.8)
74 | : primaryDarkBlue.withOpacity(0.5),
75 | fontWeight: FontWeight.w700,
76 | ),
77 | ),
78 | ),
79 |
80 | // ),
81 | ),
82 | );
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/assets/instagram.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
68 |
--------------------------------------------------------------------------------
/lib/src/views/details/people_details/components/people_bottom_tabbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../configs/configs.dart';
5 | import '../../../../controllers/utility_controller.dart';
6 |
7 | PreferredSizeWidget? peopleBottomTabbarComponent(
8 | {required List tabMenuItems}) {
9 | final _scrollController = ScrollController();
10 |
11 | return PreferredSize(
12 | preferredSize: Size(MediaQuery.of(Get.context!).size.width, kToolbarHeight),
13 | child: Container(
14 | alignment: Alignment.centerLeft,
15 | child: SingleChildScrollView(
16 | controller: _scrollController,
17 | physics: const BouncingScrollPhysics(),
18 | padding: const EdgeInsets.symmetric(horizontal: 8),
19 | scrollDirection: Axis.horizontal,
20 | child: SizedBox(
21 | height: kToolbarHeight,
22 | child: Row(
23 | children: List.from(tabMenuItems.map((e) {
24 | int index = tabMenuItems.indexOf(e);
25 | return TabbarItem(
26 | index: index,
27 | title: e,
28 | controller: _scrollController,
29 | );
30 | })),
31 | ),
32 | ),
33 | ),
34 | ),
35 | );
36 | }
37 |
38 | // tabbar item helper widget
39 | class TabbarItem extends StatelessWidget {
40 | TabbarItem(
41 | {required this.index,
42 | required this.title,
43 | required this.controller,
44 | Key? key})
45 | : super(key: key);
46 | final int index;
47 | final String title;
48 | final ScrollController controller;
49 |
50 | final _utilityController = Get.find();
51 |
52 | @override
53 | Widget build(BuildContext context) {
54 | return GestureDetector(
55 | onTap: () {
56 | _utilityController.setPeopleTabbarIndex(index);
57 | },
58 | child: Container(
59 | height: 80,
60 | color: Colors.transparent,
61 | alignment: Alignment.center,
62 | padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
63 | child:
64 | //
65 | //Obx(() =>
66 | GetBuilder(
67 | id: 'peopleBottomTabBar',
68 | init: _utilityController,
69 | builder: (controller) => Text(
70 | title,
71 | style: TextStyle(
72 | fontSize: m - 6,
73 | color: _utilityController.peopleTabbarCurrentIndex == index
74 | ? primaryDarkBlue.withOpacity(0.8)
75 | : primaryDarkBlue.withOpacity(0.5),
76 | fontWeight: FontWeight.w700,
77 | ),
78 | ),
79 | ),
80 |
81 | // ),
82 | ),
83 | );
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/lib/src/views/watchlist/watchlist_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../configs/configs.dart';
5 | import '../../controllers/utility_controller.dart';
6 | import 'components/watchlist_bottom_tabbar.dart';
7 | import 'tabs/movie_watchlist.dart';
8 | import 'tabs/tv_watchlist.dart';
9 |
10 | class WatchlistPage extends StatelessWidget {
11 | WatchlistPage({Key? key}) : super(key: key);
12 |
13 | final _utilityController = Get.find();
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return SingleChildScrollView(
18 | child: Column(
19 | crossAxisAlignment: CrossAxisAlignment.start,
20 | children: [
21 | // header
22 | Container(
23 | padding: const EdgeInsets.fromLTRB(16, 0, 16, 0),
24 | alignment: Alignment.centerLeft,
25 | height: 62,
26 | child: const Text(
27 | 'Watchlist',
28 | textAlign: TextAlign.center,
29 | style: TextStyle(
30 | fontSize: l,
31 | // color: primaryWhite,
32 | color: primaryDarkBlue,
33 | ),
34 | ),
35 | ),
36 |
37 | // tabbar
38 | WatchlistTabbarComponent(tabMenuItems: tabItems),
39 |
40 | // tabs
41 | Obx(
42 | () => tabs[_utilityController.wacthlistTabbarCurrentIndex],
43 | ),
44 | ],
45 | ),
46 | );
47 | // Scaffold(
48 | // appBar: AppBar(
49 | // elevation: 0.6,
50 | // toolbarHeight: 110,
51 | // title: const Text('Watchlist'),
52 | // bottom: searchTabbarComponent(tabMenuItems: searchTabs),
53 | // actions: [
54 | // IconButton(
55 | // onPressed: () {},
56 | // icon: const Icon(
57 | // Icons.grid_view,
58 | // size: 26,
59 | // color: primaryDarkBlue,
60 | // )),
61 | // ],
62 | // ),
63 | // body: SingleChildScrollView(
64 | // child: Column(
65 | // children: [
66 | // // tabs
67 | // Obx(
68 | // () => _searchController.searchState.value == ViewState.idle
69 | // ? _searchController.searchHistory.isEmpty
70 | // ? emptySearch()
71 | // : searchHistory()
72 | // : tabs[_utilityController.searchTabbarCurrentIndex],
73 | // ),
74 | // ],
75 | // ),
76 | // ),
77 | // );
78 | }
79 | }
80 |
81 | List tabItems = [
82 | 'Movies',
83 | 'TV Series',
84 | ];
85 |
86 | List tabs = [
87 | MovieWatchlist(),
88 | TvWatchlist(),
89 | ];
90 |
--------------------------------------------------------------------------------
/lib/src/views/home/search/tabs/people_search_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../configs/strings.dart';
5 | import '../../../../controllers/search_controller.dart';
6 | import '../../../../global/loading_spinner.dart';
7 | import '../../../../helpers/widget_builder_helper.dart';
8 |
9 | class PeopleSearchList extends StatelessWidget {
10 | PeopleSearchList({Key? key}) : super(key: key);
11 |
12 | final _searchController = Get.find();
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return GetBuilder(
17 | id: 'people_search_result',
18 | init: _searchController,
19 | initState: (_) {
20 | _searchController.setResultType(personString);
21 | },
22 | builder: (controller) => Obx(
23 | () => WidgetBuilderHelper(
24 | state: _searchController.searchState.value,
25 | onLoadingBuilder:
26 | Center(child: LoadingSpinner().fadingCircleSpinner),
27 | onErrorBuilder: const Center(
28 | child: Text('error while loading data ...'),
29 | ),
30 | onSuccessBuilder:
31 | // ListView.builder(
32 | // shrinkWrap: true,
33 | // physics: const NeverScrollableScrollPhysics(),
34 | // itemCount: _searchController.movieSearchResults.length,
35 | // itemBuilder: (context, index) => Text(
36 | // _searchController.movieSearchResults[index].title ??
37 | // "title"),
38 | // )
39 | Column(
40 | children: [
41 | // MovieList(movies: _searchController.movieSearchResults),
42 | const SizedBox(height: 18),
43 |
44 | ListView.separated(
45 | shrinkWrap: true,
46 | physics: const NeverScrollableScrollPhysics(),
47 | itemCount: _searchController.peopleSearchResults.length,
48 | separatorBuilder: (context, index) =>
49 | const SizedBox(height: 12),
50 | itemBuilder: (context, index) => GestureDetector(
51 | onTap: () {
52 | Get.offAllNamed('/people_details');
53 | },
54 | child: Text(
55 | _searchController.peopleSearchResults[index].name ??
56 | "name"),
57 | ),
58 | ),
59 | ],
60 | ),
61 | ),
62 | ));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/src/views/home/search/components/search_history_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../configs/configs.dart';
5 | import '../../../../controllers/search_controller.dart';
6 |
7 | enum SearchHistoryType { movie, tv }
8 |
9 | class SearchHistory extends StatelessWidget {
10 | SearchHistory({
11 | required this.searchHistory,
12 | required this.type,
13 | Key? key,
14 | }) : super(key: key);
15 |
16 | final List searchHistory;
17 | final SearchHistoryType type;
18 |
19 | final _searchController = Get.find();
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return Column(
24 | crossAxisAlignment: CrossAxisAlignment.start,
25 | children: [
26 | Padding(
27 | padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
28 | child: Row(
29 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
30 | children: [
31 | Text(
32 | 'Recent searches',
33 | style: TextStyle(
34 | color: primaryDarkBlue.withOpacity(0.8),
35 | fontSize: n,
36 | ),
37 | ),
38 | GestureDetector(
39 | onTap: () {
40 | type == SearchHistoryType.movie
41 | ? _searchController.clearSearchHistoryMovie()
42 | : _searchController.clearSearchHistoryTv();
43 | },
44 | child: const Text(
45 | 'Clear all',
46 | style: TextStyle(
47 | color: primaryblue,
48 | fontSize: n,
49 | ),
50 | ),
51 | ),
52 | ],
53 | ),
54 | ),
55 | ListView.builder(
56 | shrinkWrap: true,
57 | reverse: true,
58 | physics: const NeverScrollableScrollPhysics(),
59 | itemCount: searchHistory.length,
60 | itemBuilder: (BuildContext context, int index) {
61 | return ListTile(
62 | onTap: () {
63 | _searchController.search(
64 | query: searchHistory[index],
65 | resultType: _searchController.resultType.value);
66 | },
67 | title: Text(
68 | searchHistory[index],
69 | style: TextStyle(
70 | color: primaryDarkBlue.withOpacity(0.8),
71 | ),
72 | ),
73 | leading: const Icon(Icons.history),
74 | trailing: const Icon(
75 | Icons.launch,
76 | size: 18,
77 | color: primaryblue,
78 | ),
79 | );
80 | },
81 | ),
82 | ],
83 | );
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/lib/src/controllers/season_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:get/get.dart';
2 |
3 | import '../../service_locator.dart';
4 | import '../models/details/season_details_model.dart';
5 | import '../services/season_service.dart';
6 | import 'base_controller.dart';
7 |
8 | class SeasonController extends BaseController {
9 | final service = sl();
10 |
11 | var seasonModel = SeasonModel().obs;
12 | var episodeModel = Episode().obs;
13 |
14 | final _seasonState = ViewState.idle.obs;
15 | final _episodeState = ViewState.idle.obs;
16 |
17 | ViewState get seasonState => _seasonState.value;
18 | ViewState get episodeState => _episodeState.value;
19 |
20 | final _tvId = 0.obs;
21 | final _seasonNo = 1.obs;
22 | final _episodeNo = 1.obs;
23 |
24 | int get tvId => _tvId.value;
25 | int get seasonNo => _seasonNo.value;
26 | int get episodeNo => _episodeNo.value;
27 |
28 | void setTvId(int tvId) => _tvId.value = tvId;
29 | void setSeasonNo(int seasonNo) => _seasonNo.value = seasonNo;
30 | void setEpisodeNo(int episodeNo) => _episodeNo.value = episodeNo;
31 |
32 | // get season details
33 | void getSeasonDetails({
34 | required String tvId,
35 | required String seasonNo,
36 | }) async {
37 | _seasonState.value = ViewState.busy;
38 | await service
39 | .getSeasonDetails(tvId: tvId, seasonNo: seasonNo)
40 | .then((value) {
41 | seasonModel.value = SeasonModel.fromJson(value);
42 | _seasonState.value = ViewState.retrived;
43 | update(['season_details']);
44 | });
45 | }
46 |
47 | // get episode details
48 | void getEpisodeDetails({
49 | required String tvId,
50 | required String seasonNo,
51 | required String episodeNo,
52 | }) async {
53 | _episodeState.value = ViewState.busy;
54 | await service
55 | .getEpisodeDetails(tvId: tvId, seasonNo: seasonNo, episodeNo: episodeNo)
56 | .then((value) {
57 | episodeModel.value = Episode.fromJson(value);
58 | _episodeState.value = ViewState.retrived;
59 | update(['episode_details']);
60 | });
61 | }
62 |
63 | // rate episode
64 | void rateEpisode({
65 | required String tvId,
66 | required String seasonNo,
67 | required String episodeNo,
68 | required double ratingValue,
69 | }) async {
70 | await service.rateTvEpisode(
71 | tvId: tvId,
72 | seasonNo: seasonNo,
73 | episodeNo: episodeNo,
74 | ratingValue: ratingValue);
75 | update(['episode_details']);
76 | }
77 |
78 | // rate episode
79 | void deleteEpisodeRating({
80 | required String tvId,
81 | required String seasonNo,
82 | required String episodeNo,
83 | required double ratingValue,
84 | }) async {
85 | await service.deleteTvEpisodeRating(
86 | tvId: tvId,
87 | seasonNo: seasonNo,
88 | episodeNo: episodeNo,
89 | ratingValue: ratingValue);
90 | update(['episode_details']);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/lib/src/views/details/tv_details/tabs/reviews/reviews_tab.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../configs/configs.dart';
5 | import '../../../../../configs/strings.dart';
6 | import '../../../../../controllers/details_controller.dart';
7 | import '../../../../../controllers/results_controller.dart';
8 | import '../../../../../global/loading_spinner.dart';
9 | import '../../../../../helpers/widget_builder_helper.dart';
10 | import '../../../../../mixins/avatar.dart';
11 | import '../../../../../models/details/common_details_models.dart';
12 | import '../../../components/review_builder.dart';
13 |
14 | class TvReviewTab extends StatelessWidget with AvatarBuilderMixin {
15 | final _detailsController = Get.find();
16 | final _resultsController = Get.find();
17 |
18 | TvReviewTab({Key? key}) : super(key: key);
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return GetBuilder(
23 | id: 'tv_reviews',
24 | init: _detailsController,
25 | initState: (_) {
26 | _detailsController.getOtherDetails(
27 | resultType: tvString,
28 | id: _resultsController.tvId,
29 | appendTo: reviewsString);
30 | },
31 | builder: (controller) => WidgetBuilderHelper(
32 | state: _detailsController.reviewsState.value,
33 | onLoadingBuilder: LoadingSpinner().fadingCircleSpinner,
34 | onErrorBuilder: const Center(
35 | child: Text('error whlie loading data ...'),
36 | ),
37 | onSuccessBuilder: Column(
38 | crossAxisAlignment: CrossAxisAlignment.start,
39 | children: [
40 | Padding(
41 | padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 18),
42 | child: Text(
43 | '${_detailsController.reviews.value.results == null ? 0 : _detailsController.reviews.value.results!.length} Reviews',
44 | style: TextStyle(
45 | color: primaryDarkBlue.withOpacity(0.7),
46 | fontSize: n,
47 | ),
48 | ),
49 | ),
50 | ListView.separated(
51 | itemCount: _detailsController.reviews.value.results == null
52 | ? 0
53 | : _detailsController.reviews.value.results!.length,
54 | shrinkWrap: true,
55 | physics: const NeverScrollableScrollPhysics(),
56 | separatorBuilder: (context, index) => const SizedBox(height: 8),
57 | itemBuilder: (context, index) {
58 | ReviewsResult review =
59 | _detailsController.reviews.value.results![index];
60 |
61 | return ReviewBuilder(
62 | key: UniqueKey(),
63 | review: review,
64 | );
65 | },
66 | ),
67 | ],
68 | ),
69 | ),
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/src/views/details/movie_deatils/tabs/reviews/reviews_tab.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../../configs/configs.dart';
5 | import '../../../../../configs/strings.dart';
6 | import '../../../../../controllers/details_controller.dart';
7 | import '../../../../../controllers/results_controller.dart';
8 | import '../../../../../global/loading_spinner.dart';
9 | import '../../../../../helpers/widget_builder_helper.dart';
10 | import '../../../../../mixins/avatar.dart';
11 | import '../../../../../models/details/common_details_models.dart';
12 | import '../../../components/review_builder.dart';
13 |
14 | class MovieReviewTab extends StatelessWidget with AvatarBuilderMixin {
15 | final _detailsController = Get.find();
16 | final _resultsController = Get.find();
17 |
18 | MovieReviewTab({Key? key}) : super(key: key);
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return GetBuilder(
23 | id: 'movie_reviews',
24 | init: _detailsController,
25 | initState: (_) {
26 | _detailsController.getOtherDetails(
27 | resultType: movieString,
28 | id: _resultsController.movieId,
29 | appendTo: reviewsString);
30 | },
31 | builder: (controller) => WidgetBuilderHelper(
32 | state: _detailsController.reviewsState.value,
33 | onLoadingBuilder: LoadingSpinner().fadingCircleSpinner,
34 | onErrorBuilder: const Center(
35 | child: Text('error whlie loading data ...'),
36 | ),
37 | onSuccessBuilder: Column(
38 | crossAxisAlignment: CrossAxisAlignment.start,
39 | children: [
40 | Padding(
41 | padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 18),
42 | child: Text(
43 | '${_detailsController.reviews.value.results == null ? 0 : _detailsController.reviews.value.results!.length} Reviews',
44 | style: TextStyle(
45 | color: primaryDarkBlue.withOpacity(0.7),
46 | fontSize: n,
47 | ),
48 | ),
49 | ),
50 | ListView.separated(
51 | itemCount: _detailsController.reviews.value.results == null
52 | ? 0
53 | : _detailsController.reviews.value.results!.length,
54 | shrinkWrap: true,
55 | physics: const NeverScrollableScrollPhysics(),
56 | separatorBuilder: (context, index) => const SizedBox(height: 8),
57 | itemBuilder: (context, index) {
58 | ReviewsResult review =
59 | _detailsController.reviews.value.results![index];
60 |
61 | return ReviewBuilder(
62 | key: UniqueKey(),
63 | review: review,
64 | );
65 | },
66 | ),
67 | ],
68 | ),
69 | ),
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/src/views/details/people_details/tabs/about/components/about_info.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:get/get.dart';
3 | import 'package:intl/intl.dart';
4 |
5 | import '../../../../../../configs/configs.dart';
6 | import '../../../../../../models/peoples/people_model.dart';
7 |
8 | // ignore: must_be_immutable
9 | class AboutInfo extends StatelessWidget {
10 | AboutInfo({required this.people, Key? key}) : super(key: key);
11 | final PeopleModel people;
12 |
13 | /// age calculation
14 | String? birthDate;
15 | String? currentDate;
16 | String? dateNow;
17 | String? dateOfBirth;
18 | String? age;
19 |
20 | String? birthday;
21 | @override
22 | Widget build(BuildContext context) {
23 | return GetBuilder(initState: (_) {
24 | birthDate = people.birthday;
25 | currentDate = DateFormat.y().format(DateTime.now());
26 | dateNow = currentDate!.substring(0, 4);
27 | dateOfBirth = people.birthday == null ? '-' : birthDate!.substring(0, 4);
28 | age = people.birthday == null
29 | ? '-'
30 | : '${int.parse(dateNow!) - int.parse(dateOfBirth!)}';
31 | birthday = people.birthday == null
32 | ? '-'
33 | : DateFormat.yMMMMd().format(DateTime.parse(people.birthday!));
34 | }, builder: (_) {
35 | return Padding(
36 | padding: const EdgeInsets.symmetric(vertical: 8),
37 | child: Column(
38 | children: [
39 | _RowBuilder(prefixText: 'Age', suffixText: age),
40 | _RowBuilder(prefixText: 'Born on', suffixText: birthday),
41 | _RowBuilder(
42 | prefixText: 'From', suffixText: people.placeOfBirth ?? "-"),
43 | ],
44 | ),
45 | );
46 | });
47 | }
48 | }
49 |
50 | // helper
51 | class _RowBuilder extends StatelessWidget {
52 | const _RowBuilder({this.prefixText, this.suffixText, Key? key})
53 | : super(key: key);
54 | final String? prefixText;
55 | final String? suffixText;
56 | @override
57 | Widget build(BuildContext context) {
58 | return Padding(
59 | padding: const EdgeInsets.symmetric(vertical: 2),
60 | child: Row(
61 | crossAxisAlignment: CrossAxisAlignment.start,
62 | children: [
63 | Text(
64 | prefixText ?? 'prefix Text',
65 | maxLines: 2,
66 | overflow: TextOverflow.ellipsis,
67 | style: TextStyle(
68 | fontSize: n - 2,
69 | color: primaryDarkBlue.withOpacity(0.5),
70 | ),
71 | ),
72 | const SizedBox(width: 12),
73 | Expanded(
74 | child: Text(
75 | suffixText ?? 'suffix text',
76 | maxLines: 2,
77 | overflow: TextOverflow.ellipsis,
78 | style: TextStyle(
79 | fontSize: n - 2,
80 | color: primaryDarkBlue.withOpacity(0.7),
81 | ),
82 | ),
83 | ),
84 | ],
85 | ),
86 | );
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/lib/src/models/peoples/people_movie_credits.dart:
--------------------------------------------------------------------------------
1 | class PeopleMovieCreditsModel {
2 | PeopleMovieCreditsModel({
3 | this.cast,
4 | this.id,
5 | });
6 |
7 | List? cast;
8 | int? id;
9 |
10 | factory PeopleMovieCreditsModel.fromJson(Map json) =>
11 | _$PeopleMovieCreditsModelFromJson(json);
12 | }
13 |
14 | class MovieCast {
15 | MovieCast({
16 | this.adult,
17 | this.backdropPath,
18 | this.title,
19 | this.genreIds,
20 | this.originalLanguage,
21 | this.originalTitle,
22 | this.posterPath,
23 | this.video,
24 | this.voteAverage,
25 | this.voteCount,
26 | this.overview,
27 | this.releaseDate,
28 | this.id,
29 | this.popularity,
30 | this.character,
31 | this.creditId,
32 | this.order,
33 | });
34 |
35 | bool? adult;
36 | String? backdropPath;
37 | String? title;
38 | List? genreIds;
39 | String? originalLanguage;
40 | String? originalTitle;
41 | String? posterPath;
42 | bool? video;
43 | double? voteAverage;
44 | int? voteCount;
45 | String? overview;
46 | String? releaseDate;
47 | int? id;
48 | double? popularity;
49 | String? character;
50 | String? creditId;
51 | int? order;
52 |
53 | factory MovieCast.fromJson(Map json) => _$CastFromJson(json);
54 | }
55 |
56 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
57 | ///
58 | ///
59 | ///
60 | /// generated code
61 | ///
62 | ///
63 | ///
64 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
65 |
66 | PeopleMovieCreditsModel _$PeopleMovieCreditsModelFromJson(
67 | Map json) {
68 | return PeopleMovieCreditsModel(
69 | cast: json['cast'] == null
70 | ? null
71 | : List.from(json['cast'].map((e) => MovieCast.fromJson(e))),
72 | id: json['id'] as int?,
73 | );
74 | }
75 |
76 | MovieCast _$CastFromJson(Map json) {
77 | return MovieCast(
78 | adult: json['adult'] as bool?,
79 | backdropPath: json['backdrop_path'] as String?,
80 | title: json['title'] as String?,
81 | genreIds:
82 | (json['genre_ids'] as List?)?.map((e) => e as int).toList(),
83 | originalLanguage: json['original_language'] as String?,
84 | originalTitle: json['original_title'] as String?,
85 | posterPath: json['poster_path'] as String?,
86 | video: json['video'] as bool?,
87 | voteAverage: (json['vote_average'] as num?)?.toDouble(),
88 | voteCount: json['vote_count'] as int?,
89 | overview: json['overview'] as String?,
90 | releaseDate:
91 | json['release_date'] == null ? null : json['release_date'] as String,
92 | id: json['id'] as int?,
93 | popularity: (json['popularity'] as num?)?.toDouble(),
94 | character: json['character'] as String?,
95 | creditId: json['credit_id'] as String?,
96 | order: json['order'] as int?,
97 | );
98 | }
99 |
--------------------------------------------------------------------------------
/lib/src/models/peoples/people_tv_credits.dart:
--------------------------------------------------------------------------------
1 | class PeopleTvCreditsModel {
2 | PeopleTvCreditsModel({
3 | this.cast,
4 | this.id,
5 | });
6 |
7 | List? cast;
8 | int? id;
9 |
10 | factory PeopleTvCreditsModel.fromJson(Map json) =>
11 | _$PeopleTvCreditsModelFromJson(json);
12 | }
13 |
14 | class TvCast {
15 | TvCast({
16 | this.originalName,
17 | this.genreIds,
18 | this.originalLanguage,
19 | this.posterPath,
20 | this.voteAverage,
21 | this.voteCount,
22 | this.overview,
23 | this.id,
24 | this.backdropPath,
25 | this.name,
26 | this.originCountry,
27 | this.firstAirDate,
28 | this.popularity,
29 | this.character,
30 | this.creditId,
31 | this.episodeCount,
32 | });
33 |
34 | String? originalName;
35 | List? genreIds;
36 | String? originalLanguage;
37 | String? posterPath;
38 | double? voteAverage;
39 | int? voteCount;
40 | String? overview;
41 | int? id;
42 | String? backdropPath;
43 | String? name;
44 | List? originCountry;
45 | String? firstAirDate;
46 | double? popularity;
47 | String? character;
48 | String? creditId;
49 | int? episodeCount;
50 |
51 | factory TvCast.fromJson(Map json) => _$CastFromJson(json);
52 | }
53 |
54 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
55 | ///
56 | ///
57 | ///
58 | /// generated code
59 | ///
60 | ///
61 | ///
62 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
63 |
64 | PeopleTvCreditsModel _$PeopleTvCreditsModelFromJson(Map json) {
65 | return PeopleTvCreditsModel(
66 | cast: json['cast'] == null
67 | ? null
68 | : List.from(json['cast'].map((e) => TvCast.fromJson(e))),
69 | id: json['id'] as int?,
70 | );
71 | }
72 |
73 | TvCast _$CastFromJson(Map json) {
74 | return TvCast(
75 | originalName: json['original_name'] as String?,
76 | genreIds:
77 | (json['genre_ids'] as List?)?.map((e) => e as int).toList(),
78 | originalLanguage: json['original_language'] as String?,
79 | posterPath: json['poster_path'] as String?,
80 | voteAverage: (json['vote_average'] as num?)?.toDouble(),
81 | voteCount: json['vote_count'] as int?,
82 | overview: json['overview'] as String?,
83 | id: json['id'] as int?,
84 | backdropPath: json['backdrop_path'] as String?,
85 | name: json['name'] as String?,
86 | originCountry: (json['origin_country'] as List?)
87 | ?.map((e) => e as String)
88 | .toList(),
89 | firstAirDate: json['first_air_date'] == null
90 | ? null
91 | : json['first_air_date'] as String,
92 | popularity: (json['popularity'] as num?)?.toDouble(),
93 | character: json['character'] as String?,
94 | creditId: json['credit_id'] as String?,
95 | episodeCount: json['episode_count'] as int?,
96 | );
97 | }
98 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/src/views/home/search/components/search_bottom_tabbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../../configs/configs.dart';
5 | import '../../../../configs/strings.dart';
6 | import '../../../../controllers/search_controller.dart';
7 | import '../../../../controllers/utility_controller.dart';
8 |
9 | PreferredSizeWidget? searchTabbarComponent(
10 | {required List tabMenuItems}) {
11 | final _scrollController = ScrollController();
12 |
13 | return PreferredSize(
14 | preferredSize: Size(MediaQuery.of(Get.context!).size.width, kToolbarHeight),
15 | child: Container(
16 | alignment: Alignment.centerLeft,
17 | child: SingleChildScrollView(
18 | controller: _scrollController,
19 | physics: const BouncingScrollPhysics(),
20 | padding: const EdgeInsets.symmetric(horizontal: 8),
21 | scrollDirection: Axis.horizontal,
22 | child: SizedBox(
23 | height: kToolbarHeight,
24 | child: Row(
25 | children: List.from(tabMenuItems.map((e) {
26 | int index = tabMenuItems.indexOf(e);
27 | return tabbarItem(
28 | index: index,
29 | title: e,
30 | controller: _scrollController,
31 | );
32 | })),
33 | ),
34 | ),
35 | ),
36 | ),
37 | );
38 | }
39 |
40 | // helper tabbar items
41 | Widget tabbarItem({
42 | required int index,
43 | required String title,
44 | required ScrollController controller,
45 | }) {
46 | final _utilityController = Get.find();
47 | final _searchController = Get.find();
48 |
49 | return GestureDetector(
50 | onTap: () {
51 | switch (index) {
52 | case 0:
53 | _searchController.setResultType(movieString);
54 | // _searchController.movieSearchResults.clear();
55 | // _searchController.resetSearchState();
56 | break;
57 | case 1:
58 | _searchController.setResultType(tvString);
59 | // _searchController.tvSearchResults.clear();
60 | // _searchController.resetSearchState();
61 | break;
62 | case 2:
63 | _searchController.setResultType(personString);
64 | break;
65 | default:
66 | break;
67 | }
68 |
69 | _utilityController.setSearchTabbarIndex(index);
70 | },
71 | child: Container(
72 | height: 80,
73 | color: Colors.transparent,
74 | alignment: Alignment.center,
75 | padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
76 | child:
77 | //
78 | //Obx(() =>
79 | GetBuilder(
80 | id: 'searchTabBar',
81 | init: _utilityController,
82 | builder: (controller) => Text(
83 | title,
84 | style: TextStyle(
85 | fontSize: m - 6,
86 | color: _utilityController.searchTabbarCurrentIndex == index
87 | ? primaryDarkBlue.withOpacity(0.8)
88 | : primaryDarkBlue.withOpacity(0.5),
89 | fontWeight: FontWeight.w700,
90 | ),
91 | ),
92 | ),
93 |
94 | // ),
95 | ),
96 | );
97 | }
98 |
--------------------------------------------------------------------------------
/lib/src/views/watchlist/tabs/movie_watchlist.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | import '../../../configs/strings.dart';
5 | import '../../../controllers/account_controller.dart';
6 | import '../../../controllers/configuration_controller.dart';
7 | import '../../../global/loading_spinner.dart';
8 | import '../../../global/movie_thumbnail_card.dart';
9 | import '../../../helpers/widget_builder_helper.dart';
10 |
11 | class MovieWatchlist extends StatelessWidget {
12 | MovieWatchlist({Key? key}) : super(key: key);
13 |
14 | final _accountController = Get.find();
15 | final _configurationController = Get.find();
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return GetBuilder(
20 | id: 'movie_watchlist',
21 | init: _accountController,
22 | initState: (_) {
23 | _accountController.getWatchlist(mediaType: moviesString);
24 | print('initialized');
25 | },
26 | builder: (controller) => Obx(
27 | () => WidgetBuilderHelper(
28 | state: _accountController.watchlistState.value,
29 | onLoadingBuilder:
30 | Center(child: LoadingSpinner().fadingCircleSpinner),
31 | onErrorBuilder: const Center(
32 | child: Text('error while loading data ...'),
33 | ),
34 | onSuccessBuilder: Column(
35 | children: [
36 | const SizedBox(height: 28),
37 | GridView.builder(
38 | padding: const EdgeInsets.symmetric(horizontal: 14),
39 | shrinkWrap: true,
40 | physics: const NeverScrollableScrollPhysics(),
41 | itemCount: _accountController.movieWatchlist.length,
42 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
43 | crossAxisCount: 3,
44 | mainAxisSpacing: 12,
45 | crossAxisSpacing: 12,
46 | mainAxisExtent: 186,
47 | ),
48 | itemBuilder: (context, index) => AbsorbPointer(
49 | absorbing:
50 | _accountController.movieWatchlist[index].posterPath ==
51 | null
52 | ? true
53 | : false,
54 | child: MovieThumbnailCard(
55 | // onLongPress: () {
56 | // _resultController.setMovieId(
57 | // '${_accountController.movieWatchlist[index].id}');
58 | // Get.bottomSheet(BottomScrollableSheet(
59 | // movie: _accountController.movieWatchlist[index]));
60 | // },
61 | padding: const EdgeInsets.all(0),
62 | movie: _accountController.movieWatchlist[index],
63 | imageUrl:
64 | '${_configurationController.posterUrl}${_accountController.movieWatchlist[index].posterPath}',
65 | ),
66 | ),
67 | ),
68 | ],
69 | )),
70 | ),
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------