Repository: devdennysegura/flutter-netflix-clone
Branch: master
Commit: 34a03cb6bf82
Files: 61
Total size: 172.8 KB
Directory structure:
gitextract_h2gmahf_/
├── .gitignore
├── .metadata
├── README.md
├── android/
│ ├── app/
│ │ ├── build.gradle
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── netflixclone/
│ │ │ └── MainActivity.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── launch_background.xml
│ │ └── values/
│ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ └── settings.gradle
├── ios/
│ ├── Flutter/
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── flutter_export_environment.sh
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── LaunchImage.imageset/
│ │ │ ├── Contents.json
│ │ │ └── README.md
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ └── Runner.xcworkspace/
│ └── contents.xcworkspacedata
├── lib/
│ ├── app.dart
│ ├── main.dart
│ └── src/
│ ├── blocs/
│ │ └── movies_bloc.dart
│ ├── helpers/
│ │ └── config/
│ │ ├── application.dart
│ │ ├── constants.dart
│ │ ├── route_handlers.dart
│ │ └── routes.dart
│ ├── models/
│ │ ├── episode.dart
│ │ ├── item_model.dart
│ │ └── result.dart
│ ├── pages/
│ │ ├── detail/
│ │ │ ├── index.dart
│ │ │ └── state.dart
│ │ ├── filter/
│ │ │ ├── index.dart
│ │ │ └── state.dart
│ │ ├── home/
│ │ │ ├── index.dart
│ │ │ └── state.dart
│ │ ├── summary/
│ │ │ ├── index.dart
│ │ │ └── state.dart
│ │ └── video/
│ │ ├── index.dart
│ │ └── state.dart
│ ├── resources/
│ │ ├── movie_api_provider.dart
│ │ └── repository.dart
│ ├── utils/
│ │ └── theme/
│ │ ├── color.dart
│ │ └── typography.dart
│ └── widgets/
│ ├── player-controls/
│ │ ├── index.dart
│ │ └── state.dart
│ ├── player-life-cycle/
│ │ ├── index.dart
│ │ └── state.dart
│ └── tvshow-list/
│ └── index.dart
├── pubspec.yaml
└── test/
└── widget_test.dart
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
================================================
FILE: .metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 97e03104a0913886a666a36f5d11ab5763d45e6e
channel: dev
project_type: app
================================================
FILE: README.md
================================================
# Flutter Netflix Clone
Flutter App with the same style as Netflix for Android and iOS.
<div style="display:flex;flex-direction:row;justify-content: space-between">
<img src="screenshots/flutter_01.png" alt="screen_01" height="300" />
<img src="screenshots/flutter_02.png" alt="screen_02" height="300" />
<img src="screenshots/flutter_03.png" alt="screen_03" height="300" />
<img src="screenshots/flutter_04.png" alt="screen_04" height="300" />
</div>
<div style="display:flex;flex-direction:row;justify-content: space-between">
<img src="screenshots/splash.gif" alt="screen_01" height="300" />
<img src="screenshots/explore.gif" alt="screen_03" height="300" />
<img src="screenshots/info.gif" alt="screen_02" height="300" />
<img src="screenshots/intro-video.gif" alt="screen_04" height="300" />
<img src="screenshots/video-interaction.gif" alt="screen_04" height="300" />
</div>
## Server
[Netflix Flutter NodeJS & MongoDB](https://github.com/devdennysegura/nodejs-server-Flutter-Netflix-App)
## Installation
$ clone this repo
$ Install flutter dependencies
$ Install Netflix NodeJS & MongoDB
$ Run NodeJS
$ Change host String on `src/resources/movie_api_provider.dart`
$ flutter run
Thanks
------
**Denny Segura** © 2018+, Released under the [MIT License].<br>
> GitHub [@devdennysegura](https://github.com/devdennysegura) ·
> Twitter [@dennysegura3](https://twitter.com/dennysegura3)
[MIT License]: http://mit-license.org/
================================================
FILE: android/app/build.gradle
================================================
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 27
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.netflix.clone"
minSdkVersion 21
targetSdkVersion 27
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.netflixclone">
<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="Netflix"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: android/app/src/main/java/com/example/netflixclone/MainActivity.java
================================================
package com.example.netflixclone;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
================================================
FILE: android/app/src/main/res/drawable/launch_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/black" />
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item>
</layer-list>
================================================
FILE: android/app/src/main/res/values/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>
================================================
FILE: android/build.gradle
================================================
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
================================================
FILE: android/gradle.properties
================================================
org.gradle.jvmargs=-Xmx1536M
================================================
FILE: android/settings.gradle
================================================
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
================================================
FILE: ios/Flutter/AppFrameworkInfo.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
</dict>
</plist>
================================================
FILE: ios/Flutter/Debug.xcconfig
================================================
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
================================================
FILE: ios/Flutter/Release.xcconfig
================================================
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
================================================
FILE: ios/Flutter/flutter_export_environment.sh
================================================
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/dennysegura/development/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/dennysegura/Dev/Flutter/flutter-netflix-clone"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_FRAMEWORK_DIR=/Users/dennysegura/development/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
================================================
FILE: ios/Podfile
================================================
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
end
pods_ary = []
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) { |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
pods_ary.push({:name => podname, :path => podpath});
else
puts "Invalid plugin specification: #{line}"
end
}
return pods_ary
end
target 'Runner' do
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
# Flutter Pods
generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
if generated_xcode_build_settings.empty?
puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
end
generated_xcode_build_settings.map { |p|
if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
symlink = File.join('.symlinks', 'flutter')
File.symlink(File.dirname(p[:path]), symlink)
pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
end
}
# Plugin Pods
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.map { |p|
symlink = File.join('.symlinks', 'plugins', p[:name])
File.symlink(p[:path], symlink)
pod p[:name], :path => File.join(symlink, 'ios')
}
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
================================================
FILE: ios/Runner/AppDelegate.h
================================================
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : FlutterAppDelegate
@end
================================================
FILE: ios/Runner/AppDelegate.m
================================================
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
================================================
FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
================================================
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
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.
================================================
FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
================================================
FILE: ios/Runner/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
================================================
FILE: ios/Runner/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>netflix_clone</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
================================================
FILE: ios/Runner/main.m
================================================
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
================================================
FILE: ios/Runner.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
97C146F21CF9000F007C117D /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0910;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = S8QB4VV633;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.netflixClone;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.netflixClone;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.netflixClone;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: ios/Runner.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: lib/app.dart
================================================
library netflix;
// Dart Imports
import 'dart:async';
import 'dart:convert';
// Flutter imports
import 'package:http/http.dart' show Client;
import 'package:rxdart/rxdart.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// Plugins import...
import 'package:fluro/fluro.dart';
import 'package:video_player/video_player.dart';
// Router
part 'src/helpers/config/constants.dart';
part 'src/helpers/config/application.dart';
part 'src/helpers/config/routes.dart';
part 'src/helpers/config/route_handlers.dart';
// Models
part 'src/models/episode.dart';
part 'src/models/result.dart';
part 'src/models/item_model.dart';
// Blocs
part 'src/blocs/movies_bloc.dart';
// Resources
part 'src/resources/movie_api_provider.dart';
part 'src/resources/repository.dart';
part 'src/utils/theme/color.dart';
part 'src/utils/theme/typography.dart';
part 'src/pages/video/index.dart';
part 'src/pages/video/state.dart';
part 'src/pages/home/index.dart';
part 'src/pages/home/state.dart';
part 'src/pages/summary/index.dart';
part 'src/pages/summary/state.dart';
part 'src/pages/filter/index.dart';
part 'src/pages/filter/state.dart';
part 'src/pages/detail/index.dart';
part 'src/pages/detail/state.dart';
// Widgets
part 'src/widgets/tvshow-list/index.dart';
part 'src/widgets/player-life-cycle/index.dart';
part 'src/widgets/player-life-cycle/state.dart';
part 'src/widgets/player-controls/index.dart';
part 'src/widgets/player-controls/state.dart';
class Netflix extends StatelessWidget {
Netflix({Key key}) : super(key: key) {
final router = Router();
Routes.configureRoutes(router);
Application.router = router;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Netflix',
theme: ThemeData(
fontFamily: 'GoogleSans',
primaryColor: Colors.black,
),
onGenerateRoute: Application.router.generator,
home: Home(),
);
}
}
================================================
FILE: lib/main.dart
================================================
import 'package:flutter/material.dart';
import 'package:netflix_clone/app.dart';
void main() => runApp(Netflix());
================================================
FILE: lib/src/blocs/movies_bloc.dart
================================================
part of netflix;
class MoviesBloc {
final _repository = Repository();
final _moviesFetcher = PublishSubject<List<ItemModel>>();
final _movieFetcher = PublishSubject<Result>();
Observable<List<ItemModel>> get allMovies => _moviesFetcher.stream;
Observable<Result> get oneMovie => _movieFetcher.stream;
fetchAllMovies() async {
List<ItemModel> items = await _repository.fetchAllMovies();
_moviesFetcher.sink.add(items);
}
fetchOneMovie(int id) async {
Result item = await _repository.fetchMovie(id);
_movieFetcher.sink.add(item);
}
dispose() {
_moviesFetcher.close();
_movieFetcher.close();
}
}
final bloc = MoviesBloc();
================================================
FILE: lib/src/helpers/config/application.dart
================================================
part of netflix;
class Application {
static Router router;
}
================================================
FILE: lib/src/helpers/config/constants.dart
================================================
part of netflix;
Map<String, dynamic> tvShow = {
"details": {
"genres": ["Drama", "Crime"],
"cast": [
{
"person": {
"id": 15429,
"url": "http://www.tvmaze.com/people/15429/poppy-montgomery",
"name": "Poppy Montgomery",
"country": {
"name": "Australia",
"code": "AU",
"timezone": "Australia/Sydney"
},
"birthday": "1972-06-15",
"deathday": null,
"gender": "Female",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/161/402509.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/161/402509.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/15429"}
}
},
"character": {
"id": 17370,
"url":
"http://www.tvmaze.com/characters/17370/unforgettable-carrie-wells",
"name": "Carrie Wells",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/664.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/664.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/17370"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 1368,
"url": "http://www.tvmaze.com/people/1368/dylan-walsh",
"name": "Dylan Walsh",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": "1963-11-17",
"deathday": null,
"gender": "Male",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/1110.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/1110.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/1368"}
}
},
"character": {
"id": 17371,
"url":
"http://www.tvmaze.com/characters/17371/unforgettable-al-burns",
"name": "Al Burns",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/1717.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/1717.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/17371"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 15430,
"url": "http://www.tvmaze.com/people/15430/james-hiroyuki-liao",
"name": "James Hiroyuki Liao",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": null,
"deathday": null,
"gender": "Male",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/3/8721.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/3/8721.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/15430"}
}
},
"character": {
"id": 17373,
"url": "http://www.tvmaze.com/characters/17373/unforgettable-jay-lee",
"name": "Jay Lee",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/666.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/666.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/17373"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 15432,
"url": "http://www.tvmaze.com/people/15432/jane-curtin",
"name": "Jane Curtin",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": "1947-09-06",
"deathday": null,
"gender": "Female",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/3/8723.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/3/8723.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/15432"}
}
},
"character": {
"id": 176064,
"url":
"http://www.tvmaze.com/characters/176064/unforgettable-joanne-webster",
"name": "Joanne Webster",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/668.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/668.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/176064"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 10404,
"url": "http://www.tvmaze.com/people/10404/dallas-roberts",
"name": "Dallas Roberts",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": "1970-05-10",
"deathday": null,
"gender": "Male",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/161/404900.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/161/404900.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/10404"}
}
},
"character": {
"id": 17372,
"url":
"http://www.tvmaze.com/characters/17372/unforgettable-eliot-delson",
"name": "Eliot Delson",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/669.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/669.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/17372"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 15431,
"url": "http://www.tvmaze.com/people/15431/tawny-cypress",
"name": "Tawny Cypress",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": "1976-08-08",
"deathday": null,
"gender": "Female",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/130/327329.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/130/327329.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/15431"}
}
},
"character": {
"id": 17374,
"url":
"http://www.tvmaze.com/characters/17374/unforgettable-cherie-rollins-murray",
"name": "Cherie Rollins-Murray",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/667.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/667.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/17374"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 8640,
"url": "http://www.tvmaze.com/people/8640/daya-vaidya",
"name": "Daya Vaidya",
"country": {
"name": "Nepal",
"code": "NP",
"timezone": "Asia/Kathmandu"
},
"birthday": "1980-05-20",
"deathday": null,
"gender": "Female",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/108/271770.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/108/271770.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/8640"}
}
},
"character": {
"id": 17376,
"url":
"http://www.tvmaze.com/characters/17376/unforgettable-nina-inara",
"name": "Nina Inara",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/670.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/670.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/17376"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 9105,
"url": "http://www.tvmaze.com/people/9105/kevin-rankin",
"name": "Kevin Rankin",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": "1976-04-18",
"deathday": null,
"gender": "Male",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/2/7246.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/2/7246.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/9105"}
}
},
"character": {
"id": 17377,
"url":
"http://www.tvmaze.com/characters/17377/unforgettable-roe-saunders",
"name": "Roe Saunders",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/671.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/671.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/17377"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 7197,
"url": "http://www.tvmaze.com/people/7197/michael-gaston",
"name": "Michael Gaston",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": "1962-11-05",
"deathday": null,
"gender": "Male",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/3/8724.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/3/8724.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/7197"}
}
},
"character": {
"id": 17378,
"url":
"http://www.tvmaze.com/characters/17378/unforgettable-mike-costello",
"name": "Mike Costello",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/0/665.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/0/665.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/17378"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 103992,
"url": "http://www.tvmaze.com/people/103992/e-j-bonilla",
"name": "E. J. Bonilla",
"country": null,
"birthday": null,
"deathday": null,
"gender": "Male",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/32/81059.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/32/81059.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/103992"}
}
},
"character": {
"id": 209904,
"url":
"http://www.tvmaze.com/characters/209904/unforgettable-denny-padilla",
"name": "Denny Padilla",
"image": null,
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/209904"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 141871,
"url": "http://www.tvmaze.com/people/141871/la-la-anthony",
"name": "La La Anthony",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": "1979-06-25",
"deathday": null,
"gender": "Female",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/118/295914.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/118/295914.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/141871"}
}
},
"character": {
"id": 209906,
"url":
"http://www.tvmaze.com/characters/209906/unforgettable-delina-michaels",
"name": "Delina Michaels",
"image": null,
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/209906"}
}
},
"self": false,
"voice": false
},
{
"person": {
"id": 25007,
"url": "http://www.tvmaze.com/people/25007/kathy-najimy",
"name": "Kathy Najimy",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
},
"birthday": "1957-02-06",
"deathday": null,
"gender": "Female",
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_portrait/3/7855.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/3/7855.jpg"
},
"_links": {
"self": {"href": "http://api.tvmaze.com/people/25007"}
}
},
"character": {
"id": 216013,
"url":
"http://www.tvmaze.com/characters/216013/unforgettable-sandra-russo",
"name": "Sandra Russo",
"image": null,
"_links": {
"self": {"href": "http://api.tvmaze.com/characters/216013"}
}
},
"self": false,
"voice": false
}
],
"episodes": [
{
"id": 5852,
"url": "http://www.tvmaze.com/episodes/5852/unforgettable-1x01-pilot",
"name": "Pilot",
"season": 1,
"number": 1,
"airdate": "2011-09-20",
"airtime": "22:00",
"airstamp": "2011-09-21T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99519.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99519.jpg"
},
"summary":
"<p>When a murder reunites former detective Carrie Wells with her old colleague and flame, she must utilize her rare ability to revisit her every memory to catch the killer.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5852"}
}
},
{
"id": 5853,
"url": "http://www.tvmaze.com/episodes/5853/unforgettable-1x02-heroes",
"name": "Heroes",
"season": 1,
"number": 2,
"airdate": "2011-09-27",
"airtime": "22:00",
"airstamp": "2011-09-28T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99518.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99518.jpg"
},
"summary":
"<p>When a young boy witnesses a murder, Carrie must put aside the memories of her sister's death to help him lead the police to the killer.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5853"}
}
},
{
"id": 5854,
"url":
"http://www.tvmaze.com/episodes/5854/unforgettable-1x03-check-out-time",
"name": "Check Out Time",
"season": 1,
"number": 3,
"airdate": "2011-10-04",
"airtime": "22:00",
"airstamp": "2011-10-05T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99517.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99517.jpg"
},
"summary":
"<p>When a hotel maid is accused of murdering a guest who she says tried to rape her, Carrie takes matters into her own hands to uncover the truth, threatening Al's investigation.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5854"}
}
},
{
"id": 5855,
"url":
"http://www.tvmaze.com/episodes/5855/unforgettable-1x04-up-in-flames",
"name": "Up in Flames",
"season": 1,
"number": 4,
"airdate": "2011-10-11",
"airtime": "22:00",
"airstamp": "2011-10-12T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99516.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99516.jpg"
},
"summary":
"<p>When a crime scene is destroyed in an explosion moments after Carrie catches a glimpse of the room, her memories become a crucial piece of the investigation.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5855"}
}
},
{
"id": 5856,
"url":
"http://www.tvmaze.com/episodes/5856/unforgettable-1x05-with-honor",
"name": "With Honor",
"season": 1,
"number": 5,
"airdate": "2011-10-18",
"airtime": "22:00",
"airstamp": "2011-10-19T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99514.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99514.jpg"
},
"summary":
"<p>When Al's ex-partner is gunned down and the investigation reveals he may have been a dirty cop, Carrie must decide how to support him without getting too close.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5856"}
}
},
{
"id": 5857,
"url":
"http://www.tvmaze.com/episodes/5857/unforgettable-1x06-friended",
"name": "Friended",
"season": 1,
"number": 6,
"airdate": "2011-10-25",
"airtime": "22:00",
"airstamp": "2011-10-26T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99513.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99513.jpg"
},
"summary":
"<p>Carrie and Al's investigation into the murder of a hard-partying heiress takes an unexpected turn when they discover that the woman apparently didn't exist until 18 months earlier.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5857"}
}
},
{
"id": 5858,
"url":
"http://www.tvmaze.com/episodes/5858/unforgettable-1x07-road-block",
"name": "Road Block",
"season": 1,
"number": 7,
"airdate": "2011-11-01",
"airtime": "22:00",
"airstamp": "2011-11-02T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99512.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99512.jpg"
},
"summary":
"<p>Carrie's investigation into the death of her sister moves forward, but her inability to let go may hinder the search for a missing infant whose father was murdered.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5858"}
}
},
{
"id": 5859,
"url":
"http://www.tvmaze.com/episodes/5859/unforgettable-1x08-lost-things",
"name": "Lost Things",
"season": 1,
"number": 8,
"airdate": "2011-11-08",
"airtime": "22:00",
"airstamp": "2011-11-09T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99511.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99511.jpg"
},
"summary":
"<p>Carrie and Al investigate the death of a public defender, only to discover that the killer has a larger plan which may lead to a second murder.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5859"}
}
},
{
"id": 5860,
"url":
"http://www.tvmaze.com/episodes/5860/unforgettable-1x09-golden-bird",
"name": "Golden Bird",
"season": 1,
"number": 9,
"airdate": "2011-11-15",
"airtime": "22:00",
"airstamp": "2011-11-16T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99510.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99510.jpg"
},
"summary":
"<p>Al and Carrie investigate the murder of a teen who seemed to have no enemies. Meanwhile, Carrie looks to her estranged aunt for help with the investigation into her sister's murder.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5860"}
}
},
{
"id": 5861,
"url":
"http://www.tvmaze.com/episodes/5861/unforgettable-1x10-trajectories",
"name": "Trajectories",
"season": 1,
"number": 10,
"airdate": "2011-11-22",
"airtime": "22:00",
"airstamp": "2011-11-23T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99509.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99509.jpg"
},
"summary":
"<p>When a second murder suddenly occurs at an active crime scene, Al and Carrie must wade through hundreds of bystanders to uncover if it was retribution or an unrelated attack.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5861"}
}
},
{
"id": 5862,
"url":
"http://www.tvmaze.com/episodes/5862/unforgettable-1x11-spirited-away",
"name": "Spirited Away",
"season": 1,
"number": 11,
"airdate": "2011-12-13",
"airtime": "22:00",
"airstamp": "2011-12-14T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99507.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99507.jpg"
},
"summary":
"<p>When a renowned ghost hunter is killed, Carrie and Al discover that his death is linked to a chilling discovery he made on a recent assignment.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5862"}
}
},
{
"id": 5863,
"url":
"http://www.tvmaze.com/episodes/5863/unforgettable-1x12-butterfly-effect",
"name": "Butterfly Effect",
"season": 1,
"number": 12,
"airdate": "2012-01-03",
"airtime": "22:00",
"airstamp": "2012-01-04T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99506.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99506.jpg"
},
"summary":
"<p>When a construction worker with a promising future is murdered, Carrie and Al wonder if his ties to the mob were the cause of his death or an unrelated coincidence.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5863"}
}
},
{
"id": 5864,
"url":
"http://www.tvmaze.com/episodes/5864/unforgettable-1x13-brotherhood",
"name": "Brotherhood",
"season": 1,
"number": 13,
"airdate": "2012-01-10",
"airtime": "22:00",
"airstamp": "2012-01-11T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99505.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99505.jpg"
},
"summary":
"<p>Routine investigation into a college student's death takes a disturbing turn for Carrie when a suspect makes a dangerous move to outwit her memory.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5864"}
}
},
{
"id": 5865,
"url":
"http://www.tvmaze.com/episodes/5865/unforgettable-1x14-carries-caller",
"name": "Carrie's Caller",
"season": 1,
"number": 14,
"airdate": "2012-02-07",
"airtime": "22:00",
"airstamp": "2012-02-08T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99503.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99503.jpg"
},
"summary":
"<p>Serial killer with knowledge of Carrie's memory abilities taunts the Queens PD as his list of victims grows.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5865"}
}
},
{
"id": 5866,
"url":
"http://www.tvmaze.com/episodes/5866/unforgettable-1x15-the-following-sea",
"name": "The Following Sea",
"season": 1,
"number": 15,
"airdate": "2012-02-14",
"airtime": "22:00",
"airstamp": "2012-02-15T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99502.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99502.jpg"
},
"summary":
"<p>When a key witness in his murder case vanishes before testifying, Al scrambles to find her before the suspect walks free.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5866"}
}
},
{
"id": 5867,
"url":
"http://www.tvmaze.com/episodes/5867/unforgettable-1x16-heartbreak",
"name": "Heartbreak",
"season": 1,
"number": 16,
"airdate": "2012-02-21",
"airtime": "22:00",
"airstamp": "2012-02-22T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99501.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99501.jpg"
},
"summary":
"<p>Carrie and Al are at a loss to explain how a murder victim mysteriously appeared in an empty ballpark.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5867"}
}
},
{
"id": 5868,
"url":
"http://www.tvmaze.com/episodes/5868/unforgettable-1x17-blind-alleys",
"name": "Blind Alleys",
"season": 1,
"number": 17,
"airdate": "2012-02-28",
"airtime": "22:00",
"airstamp": "2012-02-29T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99500.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99500.jpg"
},
"summary":
"<p>Al and Carrie are forced to the sidelines by a negotiator when the father of a suspect that Roe shot takes members of the Queens PD, including Nina, hostage in a desperate quest for justice.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5868"}
}
},
{
"id": 5869,
"url":
"http://www.tvmaze.com/episodes/5869/unforgettable-1x18-the-comeback",
"name": "The Comeback",
"season": 1,
"number": 18,
"airdate": "2012-03-20",
"airtime": "22:00",
"airstamp": "2012-03-21T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99498.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99498.jpg"
},
"summary":
"<p>When Carrie's latest case is stalled by a powerful family's influence, she receives assistance from a lawyer who may be the mysterious caller who orchestrated a series of sniper attacks.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5869"}
}
},
{
"id": 5870,
"url":
"http://www.tvmaze.com/episodes/5870/unforgettable-1x19-allegiances",
"name": "Allegiances",
"season": 1,
"number": 19,
"airdate": "2012-03-27",
"airtime": "22:00",
"airstamp": "2012-03-28T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99497.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99497.jpg"
},
"summary":
"<p>Carrie's personal and professional lives collide when her boyfriend, the son of a mob boss, is revealed to have ties to suspects in a murder investigation.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5870"}
}
},
{
"id": 5871,
"url":
"http://www.tvmaze.com/episodes/5871/unforgettable-1x20-you-are-here",
"name": "You Are Here",
"season": 1,
"number": 20,
"airdate": "2012-04-10",
"airtime": "22:00",
"airstamp": "2012-04-11T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99496.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99496.jpg"
},
"summary":
"<p>Carrie and Al must get inside the mind of a deranged conspiracy theorist before he sets off a series of bombs.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5871"}
}
},
{
"id": 5872,
"url": "http://www.tvmaze.com/episodes/5872/unforgettable-1x21-endgame",
"name": "Endgame",
"season": 1,
"number": 21,
"airdate": "2012-05-01",
"airtime": "22:00",
"airstamp": "2012-05-02T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99495.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99495.jpg"
},
"summary":
"<p>When Walter Morgan turns up dead, Carrie must locate his killer while deflecting attention from the prime suspect - her.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5872"}
}
},
{
"id": 5873,
"url":
"http://www.tvmaze.com/episodes/5873/unforgettable-1x22-the-man-in-the-woods",
"name": "The Man in the Woods",
"season": 1,
"number": 22,
"airdate": "2012-05-08",
"airtime": "22:00",
"airstamp": "2012-05-09T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99492.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99492.jpg"
},
"summary":
"<p>When a murder occurs in Syracuse which mirrors the death of her sister, Carrie and Al travel upstate to confront the suspect.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5873"}
}
},
{
"id": 5874,
"url": "http://www.tvmaze.com/episodes/5874/unforgettable-2x01-bigtime",
"name": "Bigtime",
"season": 2,
"number": 1,
"airdate": "2013-07-28",
"airtime": "21:00",
"airstamp": "2013-07-29T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99490.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99490.jpg"
},
"summary":
"<p>Carrie and Al's reputation for closing cases results in an offer to join the Major Crimes Section of the NYPD. But when their first case - a high-profile child kidnapping - hits close to home for Carrie, it leaves her wondering if a move from Queens to Manhattan is what's best for her.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5874"}
}
},
{
"id": 5875,
"url":
"http://www.tvmaze.com/episodes/5875/unforgettable-2x02-incognito",
"name": "Incognito",
"season": 2,
"number": 2,
"airdate": "2013-08-04",
"airtime": "21:00",
"airstamp": "2013-08-05T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99488.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99488.jpg"
},
"summary":
"<p>Carrie goes undercover with a team of bank robbers when The Major Crimes Section gets a lead on the gang's leader, a notorious thief who rarely shows his face.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5875"}
}
},
{
"id": 5876,
"url":
"http://www.tvmaze.com/episodes/5876/unforgettable-2x03-day-of-the-jackie",
"name": "Day of the Jackie",
"season": 2,
"number": 3,
"airdate": "2013-08-11",
"airtime": "21:00",
"airstamp": "2013-08-12T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99487.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99487.jpg"
},
"summary":
"<p>Carrie and Al are unable to discern why a businessman was murdered in his hotel room until it's discovered that his death was collateral damage in a larger plot to assassinate a diplomat.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5876"}
}
},
{
"id": 5877,
"url":
"http://www.tvmaze.com/episodes/5877/unforgettable-2x04-memory-kings",
"name": "Memory Kings",
"season": 2,
"number": 4,
"airdate": "2013-08-18",
"airtime": "21:00",
"airstamp": "2013-08-19T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99485.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99485.jpg"
},
"summary":
"<p>When the scientist who helped Carrie understand her memory abilities is murdered, she must track down other people with the same skill in the hopes that one of them holds the key to finding the killer.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5877"}
}
},
{
"id": 5878,
"url":
"http://www.tvmaze.com/episodes/5878/unforgettable-2x05-past-tense",
"name": "Past Tense",
"season": 2,
"number": 5,
"airdate": "2013-08-25",
"airtime": "21:00",
"airstamp": "2013-08-26T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99484.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99484.jpg"
},
"summary":
"<p>Carrie and Al's latest case - the murder of an Afghani cab driver - has the potential to turn into a matter of national security when it's discovered that the victim was a government informant.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5878"}
}
},
{
"id": 5879,
"url":
"http://www.tvmaze.com/episodes/5879/unforgettable-2x06-line-up-or-shut-up",
"name": "Line Up or Shut Up",
"season": 2,
"number": 6,
"airdate": "2013-09-01",
"airtime": "21:00",
"airstamp": "2013-09-02T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99483.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99483.jpg"
},
"summary":
"<p>When a young man is found dead in an expensive sports car he was delivering to a diplomat, each clue leads Carrie and Al to yet another possible motive for the murder.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5879"}
}
},
{
"id": 5880,
"url":
"http://www.tvmaze.com/episodes/5880/unforgettable-2x07-maps-and-legends",
"name": "Maps and Legends",
"season": 2,
"number": 7,
"airdate": "2013-09-08",
"airtime": "21:00",
"airstamp": "2013-09-09T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99482.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99482.jpg"
},
"summary":
"<p>The murder of an urban explorer leads Carrie and Al to not only search for the killer, but to pick up the trail of clues the victim was following on the hunt for a treasure supposedly hidden under the streets of New York.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5880"}
}
},
{
"id": 5881,
"url":
"http://www.tvmaze.com/episodes/5881/unforgettable-2x08-till-death",
"name": "Till Death",
"season": 2,
"number": 8,
"airdate": "2014-04-04",
"airtime": "20:00",
"airstamp": "2014-04-05T00:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99480.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99480.jpg"
},
"summary":
"<p>After a wealthy couple is murdered, Carrie and Al pose as a married couple to lure in the killer, who Al believes is linked to a series of unsolved homicides he once investigated.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5881"}
}
},
{
"id": 5882,
"url":
"http://www.tvmaze.com/episodes/5882/unforgettable-2x09-flesh-and-blood",
"name": "Flesh and Blood",
"season": 2,
"number": 9,
"airdate": "2014-04-11",
"airtime": "20:00",
"airstamp": "2014-04-12T00:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99479.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99479.jpg"
},
"summary":
"<p>Carrie catches a deadly assassin who has eluded her before, but must put her personal vendetta aside when it's revealed that the woman has vital information to prevent a terrorist attack on New York City.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5882"}
}
},
{
"id": 5883,
"url": "http://www.tvmaze.com/episodes/5883/unforgettable-2x10-manhunt",
"name": "Manhunt",
"season": 2,
"number": 10,
"airdate": "2014-04-18",
"airtime": "20:00",
"airstamp": "2014-04-19T00:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99477.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99477.jpg"
},
"summary":
"<p>When a defense attorney is killed, Carrie and Al hunt for a recently paroled criminal who may be looking to settle other scores.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5883"}
}
},
{
"id": 5884,
"url":
"http://www.tvmaze.com/episodes/5884/unforgettable-2x11-east-of-islip",
"name": "East of Islip",
"season": 2,
"number": 11,
"airdate": "2014-04-25",
"airtime": "20:00",
"airstamp": "2014-04-26T00:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99476.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99476.jpg"
},
"summary":
"<p>Eliot agrees to let Carrie and Al use his Hamptons beach house in exchange for their help with a local murder investigation, but uncovering the secrets of the tight-knit community prove to be a challenge.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5884"}
}
},
{
"id": 5885,
"url":
"http://www.tvmaze.com/episodes/5885/unforgettable-2x12-omega-hour",
"name": "Omega Hour",
"season": 2,
"number": 12,
"airdate": "2014-05-02",
"airtime": "20:00",
"airstamp": "2014-05-03T00:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99475.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99475.jpg"
},
"summary":
"<p>Eliot and the mayor are trapped in an elevator by a cybercriminal who threatens to send them to their death, forcing Carrie to work from within the building to thwart his plans.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5885"}
}
},
{
"id": 5886,
"url": "http://www.tvmaze.com/episodes/5886/unforgettable-2x13-reunion",
"name": "Reunion",
"season": 2,
"number": 13,
"airdate": "2014-05-09",
"airtime": "20:00",
"airstamp": "2014-05-10T00:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99474.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99474.jpg"
},
"summary":
"<p>When a high school classmate of Carrie's is murdered at her reunion, she must revisit her memories as a teenager for clues to the motive.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5886"}
}
},
{
"id": 5887,
"url":
"http://www.tvmaze.com/episodes/5887/unforgettable-3x01-new-hundred",
"name": "New Hundred",
"season": 3,
"number": 1,
"airdate": "2014-06-29",
"airtime": "22:00",
"airstamp": "2014-06-30T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/22/57048.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/22/57048.jpg"
},
"summary":
"<p>The Secret Service enlists Carrie and Al's help when a murder investigation leads them to uncover a counterfeiting ring run by an assassin.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5887"}
}
},
{
"id": 5888,
"url":
"http://www.tvmaze.com/episodes/5888/unforgettable-3x02-the-combination",
"name": "The Combination",
"season": 3,
"number": 2,
"airdate": "2014-07-06",
"airtime": "22:00",
"airstamp": "2014-07-07T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/24/62286.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/24/62286.jpg"
},
"summary":
"<p>Carrie and Al must uncover the pieces of a championship boxer's past to figure out who was able to beat him to death without any indication that he defended himself or fought back.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5888"}
}
},
{
"id": 5889,
"url":
"http://www.tvmaze.com/episodes/5889/unforgettable-3x03-the-haircut",
"name": "The Haircut",
"season": 3,
"number": 3,
"airdate": "2014-07-13",
"airtime": "21:00",
"airstamp": "2014-07-14T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/24/62289.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/24/62289.jpg"
},
"summary":
"<p>As Lee Anne's lawsuit against the police department continues to scandalize the city, a judge suggests that she accept a settlement offer. Meanwhile, Jamie and Roy argue opposing sides of a \"stand your ground\" case.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5889"}
}
},
{
"id": 5890,
"url":
"http://www.tvmaze.com/episodes/5890/unforgettable-3x04-cashing-out",
"name": "Cashing Out",
"season": 3,
"number": 4,
"airdate": "2014-07-20",
"airtime": "21:00",
"airstamp": "2014-07-21T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99466.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99466.jpg"
},
"summary":
"<p>When Carrie recognizes a murdered city official as someone she played poker with at an underground casino, she puts her career on the line by admitting her illegal activities and volunteers to go back to the tables undercover.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5890"}
}
},
{
"id": 5891,
"url":
"http://www.tvmaze.com/episodes/5891/unforgettable-3x05-a-moveable-feast",
"name": "A Moveable Feast",
"season": 3,
"number": 5,
"airdate": "2014-07-27",
"airtime": "21:00",
"airstamp": "2014-07-28T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99467.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99467.jpg"
},
"summary":
"<p>Carrie and Al must find the connection between a murdered coast guard officer, a celebrity chef who threw him out of his restaurant and a missing block of C-4 before the explosives are used.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5891"}
}
},
{
"id": 5892,
"url":
"http://www.tvmaze.com/episodes/5892/unforgettable-3x06-stray-bullet",
"name": "Stray Bullet",
"season": 3,
"number": 6,
"airdate": "2014-08-03",
"airtime": "21:00",
"airstamp": "2014-08-04T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99469.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99469.jpg"
},
"summary":
"<p>Al becomes the prime suspect in the murder of a parolee he helped put in jail, forcing Carrie to conduct an off-the-books investigation to prove his innocence to Internal Affairs.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5892"}
}
},
{
"id": 5893,
"url":
"http://www.tvmaze.com/episodes/5893/unforgettable-3x07-throwing-shade",
"name": "Throwing Shade",
"season": 3,
"number": 7,
"airdate": "2014-08-17",
"airtime": "21:00",
"airstamp": "2014-08-18T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99470.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99470.jpg"
},
"summary":
"<p>When one of Eliot's oldest friends, the campaign manager for a mayoral candidate, is murdered, he is forced to face his past and current demons as Carrie and Al work to uncover the killer.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5893"}
}
},
{
"id": 5894,
"url":
"http://www.tvmaze.com/episodes/5894/unforgettable-3x08-the-island",
"name": "The Island",
"season": 3,
"number": 8,
"airdate": "2014-08-24",
"airtime": "21:00",
"airstamp": "2014-08-25T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99472.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99472.jpg"
},
"summary":
"<p>Carrie and Al's investigation into the death of a college dropout takes a surprising turn when they discover he had been living in an off-the-grid community on an abandoned island near Manhattan.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5894"}
}
},
{
"id": 5895,
"url":
"http://www.tvmaze.com/episodes/5895/unforgettable-3x09-admissions",
"name": "Admissions",
"season": 3,
"number": 9,
"airdate": "2014-08-30",
"airtime": "20:00",
"airstamp": "2014-08-31T00:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99473.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99473.jpg"
},
"summary":
"<p>Carrie and Al dig into the secrets of students and faculty at an elite prep school when a high-powered CEO and father of one of the students is murdered.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5895"}
}
},
{
"id": 5896,
"url":
"http://www.tvmaze.com/episodes/5896/unforgettable-3x10-fire-and-ice",
"name": "Fire and Ice",
"season": 3,
"number": 10,
"airdate": "2014-08-31",
"airtime": "21:00",
"airstamp": "2014-09-01T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99465.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99465.jpg"
},
"summary":
"<p>When Homeland Security takes over a bombing investigation from Major Crimes, Carrie doubts their conclusion that it's tied to terrorism and secretly continues her own investigation. Meanwhile, Webster makes friends with Murray's daughter.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5896"}
}
},
{
"id": 5897,
"url":
"http://www.tvmaze.com/episodes/5897/unforgettable-3x11-true-identity",
"name": "True Identity",
"season": 3,
"number": 11,
"airdate": "2014-09-07",
"airtime": "21:00",
"airstamp": "2014-09-08T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99463.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99463.jpg"
},
"summary":
"<p>Carrie and Al investigate the murder of a high-end matchmaking service employee, but each secret they uncover in the victim's life only serves to create a new possible suspect and motive.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5897"}
}
},
{
"id": 5898,
"url":
"http://www.tvmaze.com/episodes/5898/unforgettable-3x12-moving-on",
"name": "Moving On",
"season": 3,
"number": 12,
"airdate": "2014-09-14",
"airtime": "21:30",
"airstamp": "2014-09-15T01:30:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99461.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99461.jpg"
},
"summary":
"<p>Carrie and Al delve into the disturbing - and dangerous - world of celebrity obsession when a television star is found murdered in his dressing room.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5898"}
}
},
{
"id": 5899,
"url": "http://www.tvmaze.com/episodes/5899/unforgettable-3x13-doa",
"name": "DOA",
"season": 3,
"number": 13,
"airdate": "2014-09-14",
"airtime": "22:30",
"airstamp": "2014-09-15T02:30:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99460.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99460.jpg"
},
"summary":
"<p>Carrie is unable to utilize her perfect memory when an assassin doses her with a deadly poison that attacks her brain, leaving Al on a desperate hunt for the antidote.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/5899"}
}
},
{
"id": 213033,
"url":
"http://www.tvmaze.com/episodes/213033/unforgettable-4x01-blast-from-the-past",
"name": "Blast from the Past",
"season": 4,
"number": 1,
"airdate": "2015-11-27",
"airtime": "20:00",
"airstamp": "2015-11-28T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/32/81086.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/32/81086.jpg"
},
"summary":
"<p>Eddie Martin, Carrie's long-lost husband, pays a visit to Major Crimes while investigating a meth crew that is involved in something more nefarious than dealing drugs.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/213033"}
}
},
{
"id": 376859,
"url":
"http://www.tvmaze.com/episodes/376859/unforgettable-4x02-gut-check",
"name": "Gut Check",
"season": 4,
"number": 2,
"airdate": "2015-11-27",
"airtime": "21:00",
"airstamp": "2015-11-28T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/32/81087.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/32/81087.jpg"
},
"summary":
"<p>Carrie and Al are assigned to transport the star witness in a high-profile securities fraud case to Miami, while attempting to evade the killers on their trail.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/376859"}
}
},
{
"id": 426210,
"url":
"http://www.tvmaze.com/episodes/426210/unforgettable-4x03-behind-the-beat",
"name": "Behind the Beat",
"season": 4,
"number": 3,
"airdate": "2015-12-04",
"airtime": "20:00",
"airstamp": "2015-12-05T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/32/81874.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/32/81874.jpg"
},
"summary":
"<p>When a young jazz prodigy is found shot to death, Major Crime is called in to investigate. Through their hunt for a murderer, Carrie and the team navigate the halls of an ultra-competitive music conservatory before diving deep into the underbelly of the city's jazz club scene where talent, fame and love might be just enough to get you killed.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/426210"}
}
},
{
"id": 404149,
"url":
"http://www.tvmaze.com/episodes/404149/unforgettable-4x04-dollars-and-scents",
"name": "Dollars And Scents",
"season": 4,
"number": 4,
"airdate": "2015-12-11",
"airtime": "20:00",
"airstamp": "2015-12-12T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/34/86379.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/34/86379.jpg"
},
"summary":
"<p>The murder of a deli employee ignites a hunt for a criminal organization.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/404149"}
}
},
{
"id": 480202,
"url":
"http://www.tvmaze.com/episodes/480202/unforgettable-4x05-all-in",
"name": "All In",
"season": 4,
"number": 5,
"airdate": "2015-12-18",
"airtime": "20:00",
"airstamp": "2015-12-19T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/35/89290.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/35/89290.jpg"
},
"summary":
"<p>Carrie's old flame resurfaces and a murder investigation leads the team to Atlantic City.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/480202"}
}
},
{
"id": 532035,
"url":
"http://www.tvmaze.com/episodes/532035/unforgettable-4x06-the-return-of-eddie",
"name": "The Return of Eddie",
"season": 4,
"number": 6,
"airdate": "2016-01-01",
"airtime": "22:00",
"airstamp": "2016-01-02T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/37/94093.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/37/94093.jpg"
},
"summary":
"<p>Carrie and Al suspect that a tabloid cameraman's death during a convenience-store robbery may be more than just a random act of violence.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/532035"}
}
},
{
"id": 532036,
"url":
"http://www.tvmaze.com/episodes/532036/unforgettable-4x07-we-can-be-heroes",
"name": "We Can Be Heroes",
"season": 4,
"number": 7,
"airdate": "2016-01-08",
"airtime": "22:00",
"airstamp": "2016-01-09T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99459.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99459.jpg"
},
"summary":
"<p>Major Crimes determines that the kidnapping of a prominent scientist's son was not a crime of opportunity but rather a calculated scheme for revenge.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/532036"}
}
},
{
"id": 533174,
"url":
"http://www.tvmaze.com/episodes/533174/unforgettable-4x08-breathing-space",
"name": "Breathing Space",
"season": 4,
"number": 8,
"airdate": "2016-01-15",
"airtime": "21:00",
"airstamp": "2016-01-16T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99454.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99454.jpg"
},
"summary":
"<p>Suspects are plentiful in the case of a murdered aerospace engineer; the Major Crimes team learns that the victim was working on a billionaire's top-secret space mission.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/533174"}
}
},
{
"id": 546033,
"url":
"http://www.tvmaze.com/episodes/546033/unforgettable-4x09-shelter-from-the-storm",
"name": "Shelter from the Storm",
"season": 4,
"number": 9,
"airdate": "2016-01-15",
"airtime": "22:00",
"airstamp": "2016-01-16T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/39/99520.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/39/99520.jpg"
},
"summary":
"<p>Carrie, Al and a captured criminal hole up in in an abandoned precinct as a sinister storm approaches, with their captive's violent crew hot on their tail.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/546033"}
}
},
{
"id": 533175,
"url":
"http://www.tvmaze.com/episodes/533175/unforgettable-4x10-game-on",
"name": "Game On",
"season": 4,
"number": 10,
"airdate": "2016-01-22",
"airtime": "20:00",
"airstamp": "2016-01-23T01:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/40/102250.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/40/102250.jpg"
},
"summary":
"<p>Carrie and Al investigate the world of high-stakes video games and corporate espionage after a man is found dead inside a haunted-house attraction.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/533175"}
}
},
{
"id": 570267,
"url":
"http://www.tvmaze.com/episodes/570267/unforgettable-4x11-about-face",
"name": "About Face",
"season": 4,
"number": 11,
"airdate": "2016-01-22",
"airtime": "21:00",
"airstamp": "2016-01-23T02:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/40/102251.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/40/102251.jpg"
},
"summary":
"<p>Carrie and Al receive new information in the cold case of a missing surgeon, putting them on the trail of two con artists.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/570267"}
}
},
{
"id": 570269,
"url":
"http://www.tvmaze.com/episodes/570269/unforgettable-4x12-bad-company",
"name": "Bad Company",
"season": 4,
"number": 12,
"airdate": "2016-01-22",
"airtime": "22:00",
"airstamp": "2016-01-23T03:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/40/102252.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/40/102252.jpg"
},
"summary":
"<p>Capt. Russo is suspected in the murder of an informant, leading Carrie and Al into a cat-and-mouse game with a corrupt former cop.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/570269"}
}
},
{
"id": 570271,
"url":
"http://www.tvmaze.com/episodes/570271/unforgettable-4x13-paranoid-android",
"name": "Paranoid Android",
"season": 4,
"number": 13,
"airdate": "2016-01-22",
"airtime": "23:00",
"airstamp": "2016-01-23T04:00:00+00:00",
"runtime": 60,
"image": {
"medium":
"http://static.tvmaze.com/uploads/images/medium_landscape/40/102253.jpg",
"original":
"http://static.tvmaze.com/uploads/images/original_untouched/40/102253.jpg"
},
"summary":
"<p>The search for a wanted shooter triggers a flashback that conflicts with Carrie's perfect memory; the Major Crimes team uncovers a nefarious conspiracy involving trained killers.</p>",
"_links": {
"self": {"href": "http://api.tvmaze.com/episodes/570271"}
}
}
],
"year": "2011-09-20",
"description":
"<p><b>Unforgettable</b> follows Carrie Wells, an enigmatic former police detective with a rare condition that makes her memory so flawless that every place, every conversation, every moment of joy and every heartbreak is forever embedded in her mind. It's not just that she doesn't forget anything - she can't; except for one thing: the details that would help solve her sister's long-ago murder. Carrie has tried to put her past behind her, but she's unexpectedly reunited with her ex-boyfriend and partner, NYPD Detective Al Burns when she consults on a homicide case.</p>"
},
"_id": "5bedbeffa70245f2bbdd6a05",
"id": 89,
"name": "Unforgettable",
"image":
"http://static.tvmaze.com/uploads/images/original_untouched/0/663.jpg"
};
================================================
FILE: lib/src/helpers/config/route_handlers.dart
================================================
part of netflix;
var rootHandler = Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params,
[dynamic object]) {
return Home();
},
);
var summaryRouteHandler = Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params,
[dynamic object]) {
return Summary();
},
);
var detailRouteHandler = Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params,
[dynamic object]) {
return TvShow(match: object['match'], item: object['show']);
},
);
var trailerRouteHandler = Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params,
[dynamic object]) {
return Video(title: object['title']);
},
);
var filterRouteHandler = Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params,
[dynamic object]) {
return Filter(
type: object['type'],
);
},
);
================================================
FILE: lib/src/helpers/config/routes.dart
================================================
part of netflix;
class Routes {
static String root = '/';
static String summary = '/summary';
static String detail = '/detail';
static String filter = '/filter';
static String video = '/trailer';
static void configureRoutes(Router router) {
router.notFoundHandler = Handler(handlerFunc:
(BuildContext context, Map<String, List<String>> params,
[dynamic object]) {
print('ROUTE WAS NOT FOUND !!!');
return null;
});
router.define(root, handler: rootHandler);
router.define(summary, handler: summaryRouteHandler);
router.define(detail, handler: detailRouteHandler);
router.define(filter, handler: filterRouteHandler);
router.define(video, handler: trailerRouteHandler);
}
}
================================================
FILE: lib/src/models/episode.dart
================================================
part of netflix;
class Episode {
int _number;
int _season;
String _image;
String _summary;
String _name;
int _duration;
Episode.fromJson(Map<String, dynamic> parsedJson) {
RegExp exp = new RegExp(r"<[^>]*>");
_number = parsedJson['number'];
_season = parsedJson['season'];
_image = (parsedJson['image'] ?? {})['medium'];
_summary = parsedJson['summary'] != null
? parsedJson['summary'].replaceAll(exp, '')
: '';
_name = parsedJson['name'];
_duration = parsedJson['airtime'] != null &&
parsedJson['airtime'].toString().isNotEmpty
? int.parse(parsedJson['airtime'].split(':')[0])
: 0;
}
int get number => _number;
int get season => _season;
String get image => _image;
String get summary => _summary;
String get name => _name;
int get duration => _duration;
}
================================================
FILE: lib/src/models/item_model.dart
================================================
part of netflix;
class ItemModel {
String _title;
List<Result> _results = [];
ItemModel.fromJson(Map<String, dynamic> parsedJson) {
_title = parsedJson['title'];
_results = List.from(parsedJson['items'])
.map(
(r) => Result.fromJson(r),
)
.toList();
}
List<Result> get results => _results;
String get title => _title;
}
================================================
FILE: lib/src/models/result.dart
================================================
part of netflix;
class Result {
int id;
String _name;
String _image;
List<String> _genres;
List<String> _cast;
List<Episode> _episodes;
List<int> _seasons = [];
DateTime _date;
String _description;
Result.fromJson(Map<String, dynamic> parsedJson) {
_name = parsedJson['name'];
_image = parsedJson['image'];
_genres = List.from(parsedJson['details']['genres'])
.map((genre) => genre.toString())
.toList();
_cast = List.from(parsedJson['details']['cast'])
.map((cast) => cast['person']['name'].toString())
.toList();
_date = parsedJson['year'] != null
? DateTime.parse(parsedJson['year'].toString())
: DateTime.now();
RegExp exp = new RegExp(r"<[^>]*>");
_description = parsedJson['details']['description'].replaceAll(exp, '');
_episodes = List.from(parsedJson['details']['episodes'])
.map((e) => Episode.fromJson(e))
.toList();
List.from(parsedJson['details']['episodes']).forEach((s) {
int seasonNumber = int.parse(s['season'].toString());
if (!_seasons.contains(seasonNumber)) _seasons.add(seasonNumber);
});
}
String get name => _name;
String get image => _image;
List<String> get genres => _genres;
List<String> get cast => _cast;
DateTime get date => _date;
String get description => _description;
List<Episode> get episodes => _episodes;
List<int> get seasons => _seasons;
}
================================================
FILE: lib/src/pages/detail/index.dart
================================================
part of netflix;
class TvShow extends StatefulWidget {
final int match;
final Result item;
TvShow({
Key key,
this.match,
this.item,
}) : super(key: key);
@override
TvShowState createState() => TvShowState();
}
================================================
FILE: lib/src/pages/detail/state.dart
================================================
part of netflix;
class TvShowState extends State<TvShow> {
var currentSeason = 1;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
List<Episode> seasonEpisodes = widget.item.episodes
.where((Episode e) => e.season == currentSeason)
.toList();
return Scaffold(
backgroundColor: Colors.black,
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
primary: true,
expandedHeight: 430.0,
backgroundColor: Colors.black,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Container(
child: Stack(
fit: StackFit.loose,
children: <Widget>[
Container(
width: screenSize.width,
height: 220,
child: Center(
child: Container(
height: 64.0,
width: 64.0,
child: OutlineButton(
padding: EdgeInsets.all(0.0),
onPressed: () => print('play'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(32.0),
),
),
child: Container(
height: 64.0,
width: 64.0,
decoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.3),
borderRadius: BorderRadius.circular(32.0),
),
child: Icon(
Icons.play_arrow,
color: Colors.white,
size: 48.0,
),
),
),
),
),
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
widget.item.image,
),
fit: BoxFit.fitWidth,
),
),
),
Container(
width: screenSize.width,
height: 220,
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
stops: [0.1, 0.4, 1.0],
colors: [
Colors.black54,
Colors.transparent,
Colors.black
],
),
),
child: Padding(
padding: EdgeInsets.only(
left: 8.0, right: 8.0, bottom: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.item.name,
maxLines: 3,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 18.0,
),
)
],
),
),
),
),
Positioned(
top: 220,
child: Container(
padding: EdgeInsets.only(left: 8.0, right: 30.0),
width: screenSize.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'${widget.match}% de coicidencia',
textAlign: TextAlign.left,
style: TextStyle(
color: Color.fromRGBO(0, 255, 0, 0.8),
fontWeight: FontWeight.w600,
fontSize: 15.0,
),
),
Text(
widget.item.date.year.toString(),
textAlign: TextAlign.left,
style: TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.3),
fontWeight: FontWeight.w400,
fontSize: 12.0,
),
),
Text(
'16 +',
textAlign: TextAlign.left,
style: TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.3),
fontWeight: FontWeight.w400,
fontSize: 12.0,
),
),
Text(
'${widget.item.seasons.length} temporadas',
textAlign: TextAlign.left,
style: TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.3),
fontWeight: FontWeight.w400,
fontSize: 12.0,
),
),
],
),
),
),
Positioned(
top: 240,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.0),
width: screenSize.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 8.0),
child: Container(
child: Text(
widget.item.description,
overflow: TextOverflow.ellipsis,
maxLines: 3,
textAlign: TextAlign.left,
style: TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.8),
fontWeight: FontWeight.w400,
fontSize: 12.0,
),
),
),
),
Padding(
padding: EdgeInsets.only(top: 8.0),
child: Container(
child: RichText(
maxLines: 2,
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.3),
fontWeight: FontWeight.w400,
fontSize: 12.0,
),
children: <TextSpan>[
TextSpan(
text: 'Protagonizada por: ',
style: TextStyle(
fontWeight: FontWeight.bold),
),
TextSpan(
text: widget.item.cast.join(', ')),
],
),
),
),
),
Padding(
padding: EdgeInsets.only(top: 8.0),
child: Row(
children: <Widget>[
FlatButton(
textColor: Colors.white70,
onPressed: () => print('Mi Lista'),
child: Container(
height: 50.0,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(
Icons.add,
size: 32.0,
),
Text(
'Mi Lista',
style: TextStyle(fontSize: 10.0),
)
],
),
),
),
FlatButton(
textColor: Colors.white70,
onPressed: () => print('calificar'),
child: Container(
height: 50.0,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(
Icons.thumb_up,
size: 24.0,
),
Text(
'Calificar',
style: TextStyle(fontSize: 10.0),
)
],
),
),
),
FlatButton(
textColor: Colors.white70,
onPressed: () => print('Compartir'),
child: Container(
height: 50.0,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(
Icons.share,
size: 20.0,
),
Text(
'Compartir',
style: TextStyle(fontSize: 10.0),
)
],
),
),
),
],
),
),
Padding(
padding: EdgeInsets.only(top: 8.0),
child: Container(
child: Text(
'EPISODIOS',
overflow: TextOverflow.ellipsis,
maxLines: 3,
textAlign: TextAlign.left,
style: TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.8),
fontWeight: FontWeight.w700,
fontSize: 15.0,
),
),
),
),
FlatButton(
padding: EdgeInsets.all(0.0),
onPressed: widget.item.seasons.length > 1
? () => print('cambiando temporada')
: null,
child: Row(
children: <Widget>[
Text(
'Temporada $currentSeason',
overflow: TextOverflow.ellipsis,
maxLines: 3,
textAlign: TextAlign.left,
style: TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.6),
fontWeight: FontWeight.w500,
fontSize: 15.0,
),
),
(widget.item.seasons.length > 1
? Padding(
padding: EdgeInsets.only(left: 8.0),
child: Icon(
Icons.arrow_drop_down,
color: Color.fromRGBO(
255, 255, 255, 0.6),
),
)
: Container())
],
),
),
],
),
),
),
],
),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Container(
margin: EdgeInsets.only(bottom: 16.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(right: 8.0),
width: 150.0,
height: 90.0,
decoration: BoxDecoration(
image: DecorationImage(
image:
NetworkImage(seasonEpisodes[index].image),
),
),
child: Center(
child: Container(
height: 32.0,
width: 32.0,
child: OutlineButton(
padding: EdgeInsets.all(0.0),
onPressed: () => print('play'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(32.0),
),
),
child: Container(
height: 32.0,
width: 32.0,
decoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.3),
borderRadius:
BorderRadius.circular(16.0),
),
child: Icon(
Icons.play_arrow,
color: Colors.white,
size: 24.0,
),
),
),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'${index + 1}. ${seasonEpisodes[index].name}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14.0,
color: Color.fromRGBO(255, 255, 255, 0.8),
),
),
Text(
'${seasonEpisodes[index].duration}m',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12.0,
color: Color.fromRGBO(255, 255, 255, 0.3),
),
)
],
)
],
),
Text(
seasonEpisodes[index].summary,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 10.0,
color: Color.fromRGBO(255, 255, 255, 0.3),
),
)
],
),
),
childCount: seasonEpisodes.length,
),
)
],
),
);
}
}
================================================
FILE: lib/src/pages/filter/index.dart
================================================
part of netflix;
class Filter extends StatefulWidget {
final String title;
final String type;
Filter({
Key key,
this.title,
this.type,
}) : super(key: key);
@override
FilterState createState() => FilterState();
}
================================================
FILE: lib/src/pages/filter/state.dart
================================================
part of netflix;
class FilterState extends State<Filter> {
String filterSelected;
List<String> options = ['Series', 'Películas', 'Mi-lista'];
dynamic tvShow = {
"details": {
"genres": ["Drama", "Crime"],
"year": "2011-09-20",
"description":
"<p><b>Unforgettable</b> follows Carrie Wells, an enigmatic former police detective with a rare condition that makes her memory so flawless that every place, every conversation, every moment of joy and every heartbreak is forever embedded in her mind. It's not just that she doesn't forget anything - she can't; except for one thing: the details that would help solve her sister's long-ago murder. Carrie has tried to put her past behind her, but she's unexpectedly reunited with her ex-boyfriend and partner, NYPD Detective Al Burns when she consults on a homicide case.</p>"
},
"_id": "5bedbf00a70245f2bbdd6a64",
"id": 89,
"name": "Unforgettable",
"image":
"http://static.tvmaze.com/uploads/images/original_untouched/0/663.jpg"
};
@override
void initState() {
filterSelected = widget.type;
super.initState();
}
@override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
primary: true,
expandedHeight: screenSize.height * 0.65,
backgroundColor: Colors.black,
leading: Image.asset('assets/images/netflix_icon.png'),
// titleSpacing: 20.0,
title: Title(
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Hero(
tag: widget.type,
child: FlatButton(
onPressed: () => print(widget.type),
child: Text(
widget.type.replaceAll('-', ' '),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
),
),
),
],
),
),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Container(
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Image.network(
tvShow['image'],
fit: BoxFit.cover,
),
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
stops: [0.1, 0.6, 1.0],
colors: [
Colors.black54,
Colors.transparent,
Colors.black
],
),
),
),
],
),
),
),
),
],
);
}
}
================================================
FILE: lib/src/pages/home/index.dart
================================================
part of netflix;
class Home extends StatefulWidget {
Home({Key key, this.title}) : super(key: key);
final String title;
@override
HomeState createState() => HomeState();
}
================================================
FILE: lib/src/pages/home/state.dart
================================================
part of netflix;
class HomeState extends State<Home> with SingleTickerProviderStateMixin {
TabController controller;
@override
void initState() {
super.initState();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light
.copyWith(statusBarColor: Colors.transparent));
controller = TabController(length: 5, initialIndex: 0, vsync: this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
bottomNavigationBar: TabBar(
labelStyle: TextStyle(fontSize: 10.0),
indicatorWeight: 0.1,
controller: controller,
tabs: <Widget>[
Tab(text: 'Inicio', icon: Icon(Icons.home)),
Tab(text: 'Buscar', icon: Icon(Icons.search)),
Tab(text: 'Próximamente', icon: Icon(Icons.ondemand_video)),
Tab(text: 'Descargas', icon: Icon(Icons.file_download)),
Tab(text: 'Más', icon: Icon(Icons.menu)),
],
),
body: TabBarView(
controller: controller,
physics: NeverScrollableScrollPhysics(),
children: <Widget>[
Summary(),
Summary(),
Summary(),
Summary(),
Summary(),
],
),
);
}
}
================================================
FILE: lib/src/pages/summary/index.dart
================================================
part of netflix;
class Summary extends StatefulWidget {
Summary({Key key, this.title}) : super(key: key);
final String title;
@override
SummaryState createState() => SummaryState();
}
================================================
FILE: lib/src/pages/summary/state.dart
================================================
part of netflix;
class SummaryState extends State<Summary> {
void goTo(String type) {
Application.router.navigateTo(
context,
'${Routes.filter}',
transition: TransitionType.nativeModal,
transitionDuration: const Duration(milliseconds: 200),
object: {'type': type},
);
}
void goToDetail(Result item, int match) {
Application.router.navigateTo(
context,
'${Routes.detail}',
transition: TransitionType.inFromRight,
transitionDuration: const Duration(milliseconds: 200),
object: {'match': match, 'show': item},
);
}
void showTrailer() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
]).then((e) {
Application.router.navigateTo(
context,
Routes.video,
object: {'title': 'Unforgettable'},
transition: TransitionType.inFromBottom,
transitionDuration: const Duration(milliseconds: 200),
);
});
}
List<Widget> renderMainGenres() {
List<Widget> genres = List.from(tvShow['details']['genres'].map((g) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
g,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 12.0,
),
),
);
}).toList());
return genres;
}
Widget renderTitle(String tag, String text) {
return Hero(
tag: tag,
child: FlatButton(
onPressed: () => goTo(tag),
child: Text(
text,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
),
),
);
}
@override
Widget build(BuildContext context) {
final Size screenSize = MediaQuery.of(context).size;
final Result show = Result.fromJson(tvShow);
bloc.fetchAllMovies();
return StreamBuilder(
stream: bloc.allMovies,
builder: (context, AsyncSnapshot<List<ItemModel>> snapshot) {
if (snapshot.hasData) {
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
primary: true,
expandedHeight: screenSize.height * 0.65,
backgroundColor: Colors.black,
leading: Image.asset('assets/images/netflix_icon.png'),
titleSpacing: 20.0,
title: Title(
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
renderTitle('Series', 'Series'),
renderTitle('Películas', 'Películas'),
renderTitle('Mi-lista', 'Mi lista'),
],
),
),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Container(
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Image.network(show.image, fit: BoxFit.cover),
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
stops: [0.1, 0.6, 1.0],
colors: [
Colors.black54,
Colors.transparent,
Colors.black
],
),
),
child: Container(
height: 40.0,
width: screenSize.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 8.0),
child: Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
width: 3.0,
color:
Color.fromRGBO(185, 3, 12, 1.0),
),
),
),
child: Text(
tvShow['name'].replaceAll(' ', '\n'),
maxLines: 3,
textAlign: TextAlign.left,
style: TextStyle(
height: 0.65,
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 30.0,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: renderMainGenres(),
),
Container(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
textColor: Colors.white,
child: Column(
children: <Widget>[
Icon(Icons.add),
Text(
'Mi lista',
style: TextStyle(
fontSize: 10.0,
fontWeight: FontWeight.w300),
),
],
),
onPressed: () => print('mi lista'),
),
RaisedButton(
textColor: Colors.black,
color: Colors.white,
child: Row(
children: <Widget>[
Icon(Icons.play_arrow),
Text(
'Reproducir',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
),
],
),
onPressed: showTrailer,
),
FlatButton(
textColor: Colors.white,
child: Column(
children: <Widget>[
Icon(Icons.info_outline),
Text(
'Información',
style: TextStyle(
fontSize: 10.0,
fontWeight: FontWeight.w300),
),
],
),
onPressed: () => goToDetail(show, 99),
),
],
),
)
],
),
),
),
],
),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ShowsList(
items: snapshot.data[index].results,
onTap: goToDetail,
title: snapshot.data[index].title,
),
childCount: snapshot.data.length,
),
)
],
);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Color.fromRGBO(219, 0, 0, 1.0)),
));
},
);
}
}
================================================
FILE: lib/src/pages/video/index.dart
================================================
part of netflix;
class Video extends StatefulWidget {
final String title;
Video({Key key, this.title}) : super(key: key);
@override
VideoState createState() => VideoState();
}
================================================
FILE: lib/src/pages/video/state.dart
================================================
part of netflix;
class VideoState extends State<Video> {
VideoPlayerController vcontroller;
bool controlVisible;
Timer timer;
@override
void initState() {
controlVisible = true;
vcontroller = VideoPlayerController.asset('assets/video/promo.mp4');
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark.copyWith(
statusBarColor: Colors.transparent,
));
super.initState();
autoHide();
}
@override
void deactivate() {
super.deactivate();
}
@override
void dispose() {
vcontroller?.dispose();
timer?.cancel();
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitDown,
DeviceOrientation.portraitUp,
]);
super.dispose();
}
void handlerGesture() {
setState(() {
controlVisible = !controlVisible;
});
autoHide();
}
void autoHide() {
if (controlVisible) {
timer = Timer(
Duration(seconds: 5), () => setState(() => controlVisible = false));
} else {
timer?.cancel();
}
}
@override
Widget build(BuildContext context) {
final aspectRatio = 0.75;
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
fit: StackFit.expand,
children: <Widget>[
PlayerLifeCycle(
vcontroller,
(BuildContext context, VideoPlayerController controller) =>
AspectRatio(
aspectRatio: aspectRatio,
child: VideoPlayer(vcontroller),
),
),
GestureDetector(
child: PlayerControl(
vcontroller,
visible: controlVisible,
title: widget.title,
),
onTap: handlerGesture,
),
],
),
);
}
}
================================================
FILE: lib/src/resources/movie_api_provider.dart
================================================
part of netflix;
class MovieApiProvider {
final String host = 'https://your-host';
Client client = Client();
Future<List<ItemModel>> fetchMovieList() async {
final response = await client.get('$host/api/Home');
if (response.statusCode == 200) {
return List.from(json.decode(response.body))
.map((m) => ItemModel.fromJson(m))
.toList();
} else {
throw Exception('Failed to load post');
}
}
Future<Result> fetchOne(int id) async {
final response = await client.get('$host/api/show/$id');
if (response.statusCode == 200) {
return Result.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load post');
}
}
}
================================================
FILE: lib/src/resources/repository.dart
================================================
part of netflix;
class Repository {
final moviesApiProvider = MovieApiProvider();
Future<List<ItemModel>> fetchAllMovies() => moviesApiProvider.fetchMovieList();
Future<Result> fetchMovie(int id) => moviesApiProvider.fetchOne(id);
}
================================================
FILE: lib/src/utils/theme/color.dart
================================================
part of netflix;
class Palette {
static const Color dune = Color.fromRGBO(40, 38, 35, 1.0);
static const Color yellowOrange = Color.fromRGBO(250, 186, 59, 1.0);
}
================================================
FILE: lib/src/utils/theme/typography.dart
================================================
part of netflix;
class TextTheme {
static const TextStyle heading1 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 96.0,
letterSpacing: -1.5,
fontWeight: FontWeight.w300,
);
static const TextStyle heading2 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 60.0,
letterSpacing: -0.5,
fontWeight: FontWeight.w300,
);
static const TextStyle heading3 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 48.0,
letterSpacing: 0.0,
fontWeight: FontWeight.w400,
);
static const TextStyle heading4 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 34.0,
letterSpacing: 0.25,
fontWeight: FontWeight.w400,
);
static const TextStyle heading5 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 24.0,
letterSpacing: 0.0,
fontWeight: FontWeight.w400,
);
static const TextStyle heading6 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 20.0,
letterSpacing: 0.15,
fontWeight: FontWeight.w600,
);
static const TextStyle subtitle1 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 16.0,
letterSpacing: 0.15,
fontWeight: FontWeight.w400,
);
static const TextStyle subtitle2 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 14.0,
letterSpacing: 0.1,
fontWeight: FontWeight.w600,
);
static const TextStyle body1 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 16.0,
letterSpacing: 0.5,
fontWeight: FontWeight.w400,
);
static const TextStyle body2 = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 14.0,
letterSpacing: 0.25,
fontWeight: FontWeight.w400,
);
static const TextStyle button = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 14.0,
letterSpacing: 1.25,
fontWeight: FontWeight.w600,
);
static const TextStyle caption = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 12.0,
letterSpacing: 0.4,
fontWeight: FontWeight.w400,
);
static const TextStyle overline = TextStyle(
fontFamily: 'GoogleSans',
fontSize: 10.0,
letterSpacing: 1.5,
fontWeight: FontWeight.w400,
);
}
================================================
FILE: lib/src/widgets/player-controls/index.dart
================================================
part of netflix;
class PlayerControl extends StatefulWidget {
final VideoPlayerController controller;
final String title;
final bool visible;
PlayerControl(this.controller, {this.visible, this.title, Key key}) : super(key: key);
@override
PlayerControlState createState() => PlayerControlState();
}
================================================
FILE: lib/src/widgets/player-controls/state.dart
================================================
part of netflix;
class PlayerControlState extends State<PlayerControl> {
VideoPlayerController get controller => widget.controller;
String get title => widget.title ?? '';
bool get visible => widget.visible ?? false;
@override
void initState() {
super.initState();
Timer(Duration(milliseconds: 500), init);
}
@override
void dispose() {
super.dispose();
}
void init() {
if (mounted)
controller.addListener(() {
setState(() {});
});
}
void replay(double min, double current) {
double anchor = current - 10000;
controller
.seekTo(Duration(milliseconds: (anchor < min ? min : anchor).round()));
}
void forward(double max, double current) {
double anchor = current + 10000;
controller
.seekTo(Duration(milliseconds: (anchor > max ? max : anchor).round()));
}
void playcontinue() {
if (controller.value.isPlaying) {
controller.pause();
} else {
controller.play();
}
}
@override
Widget build(BuildContext context) {
Duration duration = controller != null &&
controller.value != null &&
controller.value.duration != null
? controller.value.duration
: null;
Duration position = controller != null && controller.value != null
? controller.value.position
: null;
int timeDuration = duration != null && position != null
? duration.inMilliseconds - position.inMilliseconds
: 0;
Duration timeline = duration != null && position != null
? Duration(milliseconds: (timeDuration < 0 ? 0 : timeDuration))
: null;
String durationText =
timeline != null ? timeline.toString().split('.').first : '';
final Size screenSize = MediaQuery.of(context).size;
double currentValue = position != null
? controller.value.position.inMilliseconds?.toDouble() ?? 0.0
: 0.0;
final double minValue = 0.0;
double maxValue = duration != null
? controller.value.duration.inMilliseconds?.toDouble()
: 0.0;
return AnimatedContainer(
duration: Duration(milliseconds: 500),
height: screenSize.height,
width: screenSize.width,
color: Color.fromRGBO(0, 0, 0, 0.3),
padding:
EdgeInsets.only(bottom: 20.0, left: 20.0, right: 20.0, top: 10.0),
child: Opacity(
opacity: visible ? 1 : 0,
child: visible
? Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
width: screenSize.width,
height: 30.0,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
IconButton(
padding: EdgeInsets.all(0.0),
color: Colors.white,
icon: Icon(Icons.arrow_back),
onPressed: () => Application.router.pop(context),
),
Container(
width: screenSize.width - 100.0,
child: Center(
child: Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: 14.0,
),
),
),
)
],
),
),
Container(
width: screenSize.width,
height: 268.0,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
color: Colors.white,
iconSize: 40.0,
icon: Icon(Icons.replay_10),
onPressed: () => replay(minValue, currentValue),
),
IconButton(
color: Colors.white,
iconSize: 80.0,
icon: Icon(controller.value.isPlaying
? Icons.pause
: Icons.play_arrow),
onPressed: playcontinue,
),
IconButton(
color: Colors.white,
iconSize: 40.0,
icon: Icon(Icons.forward_10),
onPressed: () => forward(maxValue, currentValue),
)
],
),
),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
width: screenSize.width - 90.0,
child: Slider(
activeColor: Color.fromRGBO(219, 0, 0, 1.0),
inactiveColor: Color.fromRGBO(86, 77, 77, 1.0),
value:
currentValue > maxValue ? maxValue : currentValue,
onChanged: (double value) => controller
.seekTo(Duration(milliseconds: value.round())),
min: minValue,
max: maxValue,
),
),
Text(
durationText,
style: TextStyle(fontSize: 12.0, color: Colors.white),
)
],
),
],
)
: Container(),
),
);
}
}
================================================
FILE: lib/src/widgets/player-life-cycle/index.dart
================================================
part of netflix;
typedef Widget VideoWidgetBuilder(
BuildContext context, VideoPlayerController controller);
class PlayerLifeCycle extends StatefulWidget {
final VideoPlayerController controller;
final VideoWidgetBuilder childBuilder;
PlayerLifeCycle(this.controller, this.childBuilder, {Key key})
: super(key: key);
@override
PlayerLifeCycleState createState() => PlayerLifeCycleState();
}
================================================
FILE: lib/src/widgets/player-life-cycle/state.dart
================================================
part of netflix;
class PlayerLifeCycleState extends State<PlayerLifeCycle> {
VideoPlayerController get controller => widget.controller;
@override
void initState() {
super.initState();
controller.addListener(() {
if (controller.value.hasError) {
print(controller.value.errorDescription);
}
});
controller.initialize();
controller.setLooping(false);
controller.play();
}
@override
void deactivate() {
super.deactivate();
}
@override
Widget build(BuildContext context) {
return widget.childBuilder(context, controller);
}
}
================================================
FILE: lib/src/widgets/tvshow-list/index.dart
================================================
part of netflix;
class ShowsList extends StatelessWidget {
final ScrollController controller = ScrollController();
final String title;
final List<Result> items;
final Function onTap;
ShowsList({
this.title,
this.items,
this.onTap,
});
List<Widget> renderItems() {
return items.map((item) {
return InkWell(
onTap: () => onTap(item, 99),
child: Container(
margin: EdgeInsets.symmetric(horizontal: 2.5),
width: 120.0,
height: 140.0,
child: Image.network(item.image, fit: BoxFit.cover),
),
);
}).toList();
}
@override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.only(top: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
title,
textAlign: TextAlign.start,
style: TextStyle(color: Colors.white),
),
SingleChildScrollView(
controller: controller,
scrollDirection: Axis.horizontal,
child: Row(
children: renderItems(),
),
)
],
),
);
}
}
================================================
FILE: pubspec.yaml
================================================
name: netflix_clone
description: A new Flutter project.
version: 1.0.0+1
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
rxdart: ^0.20.0
http: ^0.12.0
video_player: ^0.8.0
fluro:
git: https://github.com/devdennysegura/fluro.git
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
assets:
- assets/images/1.jpg
- assets/images/2.jpg
- assets/images/3.jpg
- assets/images/default-image.png
- assets/images/Netflix-logo.png
- assets/images/netflix_icon.png
- assets/images/1.5x/netflix_icon.png
- assets/images/2.0x/netflix_icon.png
- assets/images/3.0x/netflix_icon.png
- assets/images/4.0x/netflix_icon.png
- assets/images/user.png
- assets/video/promo.mp4
fonts:
- family: GoogleSans
fonts:
- asset: assets/fonts/product-sans/regular.ttf
weight: 400
- asset: assets/fonts/product-sans/bold.ttf
weight: 700
- asset: assets/fonts/product-sans/bold-italic.ttf
style: italic
weight: 700
- asset: assets/fonts/product-sans/medium.ttf
weight: 600
- asset: assets/fonts/product-sans/medium-italic.ttf
style: italic
weight: 600
- asset: assets/fonts/product-sans/italic.ttf
style: italic
================================================
FILE: test/widget_test.dart
================================================
// // This is a basic Flutter widget test.
// //
// // To perform an interaction with a widget in your test, use the WidgetTester
// // utility that Flutter provides. For example, you can send tap and scroll
// // gestures. You can also use WidgetTester to find child widgets in the widget
// // tree, read text, and verify that the values of widget properties are correct.
// import 'package:flutter/material.dart';
// import 'package:flutter_test/flutter_test.dart';
// import 'package:netflix_clone/main.dart';
// void main() {
// testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// // Build our app and trigger a frame.
// await tester.pumpWidget(MyApp());
// // Verify that our counter starts at 0.
// expect(find.text('0'), findsOneWidget);
// expect(find.text('1'), findsNothing);
// // Tap the '+' icon and trigger a frame.
// await tester.tap(find.byIcon(Icons.add));
// await tester.pump();
// // Verify that our counter has incremented.
// expect(find.text('0'), findsNothing);
// expect(find.text('1'), findsOneWidget);
// });
// }
gitextract_h2gmahf_/
├── .gitignore
├── .metadata
├── README.md
├── android/
│ ├── app/
│ │ ├── build.gradle
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── netflixclone/
│ │ │ └── MainActivity.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── launch_background.xml
│ │ └── values/
│ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ └── settings.gradle
├── ios/
│ ├── Flutter/
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── flutter_export_environment.sh
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── LaunchImage.imageset/
│ │ │ ├── Contents.json
│ │ │ └── README.md
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ └── Runner.xcworkspace/
│ └── contents.xcworkspacedata
├── lib/
│ ├── app.dart
│ ├── main.dart
│ └── src/
│ ├── blocs/
│ │ └── movies_bloc.dart
│ ├── helpers/
│ │ └── config/
│ │ ├── application.dart
│ │ ├── constants.dart
│ │ ├── route_handlers.dart
│ │ └── routes.dart
│ ├── models/
│ │ ├── episode.dart
│ │ ├── item_model.dart
│ │ └── result.dart
│ ├── pages/
│ │ ├── detail/
│ │ │ ├── index.dart
│ │ │ └── state.dart
│ │ ├── filter/
│ │ │ ├── index.dart
│ │ │ └── state.dart
│ │ ├── home/
│ │ │ ├── index.dart
│ │ │ └── state.dart
│ │ ├── summary/
│ │ │ ├── index.dart
│ │ │ └── state.dart
│ │ └── video/
│ │ ├── index.dart
│ │ └── state.dart
│ ├── resources/
│ │ ├── movie_api_provider.dart
│ │ └── repository.dart
│ ├── utils/
│ │ └── theme/
│ │ ├── color.dart
│ │ └── typography.dart
│ └── widgets/
│ ├── player-controls/
│ │ ├── index.dart
│ │ └── state.dart
│ ├── player-life-cycle/
│ │ ├── index.dart
│ │ └── state.dart
│ └── tvshow-list/
│ └── index.dart
├── pubspec.yaml
└── test/
└── widget_test.dart
SYMBOL INDEX (73 symbols across 28 files)
FILE: android/app/src/main/java/com/example/netflixclone/MainActivity.java
class MainActivity (line 7) | public class MainActivity extends FlutterActivity {
method onCreate (line 8) | @Override
FILE: lib/app.dart
class Netflix (line 55) | class Netflix extends StatelessWidget {
method build (line 63) | Widget build(BuildContext context)
FILE: lib/main.dart
function main (line 4) | void main()
FILE: lib/src/blocs/movies_bloc.dart
class MoviesBloc (line 3) | class MoviesBloc {
FILE: lib/src/helpers/config/application.dart
class Application (line 3) | class Application {
FILE: lib/src/helpers/config/routes.dart
class Routes (line 3) | class Routes {
method configureRoutes (line 10) | void configureRoutes(Router router)
FILE: lib/src/models/episode.dart
class Episode (line 3) | class Episode {
FILE: lib/src/models/item_model.dart
class ItemModel (line 3) | class ItemModel {
FILE: lib/src/models/result.dart
class Result (line 3) | class Result {
FILE: lib/src/pages/detail/index.dart
class TvShow (line 3) | class TvShow extends StatefulWidget {
method createState (line 14) | TvShowState createState()
FILE: lib/src/pages/detail/state.dart
class TvShowState (line 3) | class TvShowState extends State<TvShow> {
method initState (line 6) | void initState()
method build (line 11) | Widget build(BuildContext context)
FILE: lib/src/pages/filter/index.dart
class Filter (line 3) | class Filter extends StatefulWidget {
method createState (line 13) | FilterState createState()
FILE: lib/src/pages/filter/state.dart
class FilterState (line 3) | class FilterState extends State<Filter> {
method initState (line 20) | void initState()
method build (line 26) | Widget build(BuildContext context)
FILE: lib/src/pages/home/index.dart
class Home (line 3) | class Home extends StatefulWidget {
method createState (line 8) | HomeState createState()
FILE: lib/src/pages/home/state.dart
class HomeState (line 3) | class HomeState extends State<Home> with SingleTickerProviderStateMixin {
method initState (line 7) | void initState()
method build (line 15) | Widget build(BuildContext context)
FILE: lib/src/pages/summary/index.dart
class Summary (line 3) | class Summary extends StatefulWidget {
method createState (line 8) | SummaryState createState()
FILE: lib/src/pages/summary/state.dart
class SummaryState (line 3) | class SummaryState extends State<Summary> {
method goTo (line 5) | void goTo(String type)
method goToDetail (line 15) | void goToDetail(Result item, int match)
method showTrailer (line 25) | void showTrailer()
method renderMainGenres (line 40) | List<Widget> renderMainGenres()
method renderTitle (line 57) | Widget renderTitle(String tag, String text)
method build (line 75) | Widget build(BuildContext context)
FILE: lib/src/pages/video/index.dart
class Video (line 3) | class Video extends StatefulWidget {
method createState (line 8) | VideoState createState()
FILE: lib/src/pages/video/state.dart
class VideoState (line 3) | class VideoState extends State<Video> {
method initState (line 9) | void initState()
method deactivate (line 20) | void deactivate()
method dispose (line 25) | void dispose()
method handlerGesture (line 35) | void handlerGesture()
method autoHide (line 42) | void autoHide()
method build (line 52) | Widget build(BuildContext context)
FILE: lib/src/resources/movie_api_provider.dart
class MovieApiProvider (line 3) | class MovieApiProvider {
method fetchMovieList (line 7) | Future<List<ItemModel>> fetchMovieList()
method fetchOne (line 18) | Future<Result> fetchOne(int id)
FILE: lib/src/resources/repository.dart
class Repository (line 3) | class Repository {
method fetchAllMovies (line 6) | Future<List<ItemModel>> fetchAllMovies()
method fetchMovie (line 7) | Future<Result> fetchMovie(int id)
FILE: lib/src/utils/theme/color.dart
class Palette (line 3) | class Palette {
FILE: lib/src/utils/theme/typography.dart
class TextTheme (line 3) | class TextTheme {
FILE: lib/src/widgets/player-controls/index.dart
class PlayerControl (line 3) | class PlayerControl extends StatefulWidget {
method createState (line 11) | PlayerControlState createState()
FILE: lib/src/widgets/player-controls/state.dart
class PlayerControlState (line 3) | class PlayerControlState extends State<PlayerControl> {
method initState (line 9) | void initState()
method dispose (line 15) | void dispose()
method init (line 19) | void init()
method replay (line 26) | void replay(double min, double current)
method forward (line 32) | void forward(double max, double current)
method playcontinue (line 38) | void playcontinue()
method build (line 47) | Widget build(BuildContext context)
FILE: lib/src/widgets/player-life-cycle/index.dart
type Widget (line 3) | typedef Widget VideoWidgetBuilder(
class PlayerLifeCycle (line 6) | class PlayerLifeCycle extends StatefulWidget {
method createState (line 14) | PlayerLifeCycleState createState()
FILE: lib/src/widgets/player-life-cycle/state.dart
class PlayerLifeCycleState (line 3) | class PlayerLifeCycleState extends State<PlayerLifeCycle> {
method initState (line 7) | void initState()
method deactivate (line 20) | void deactivate()
method build (line 25) | Widget build(BuildContext context)
FILE: lib/src/widgets/tvshow-list/index.dart
class ShowsList (line 3) | class ShowsList extends StatelessWidget {
method renderItems (line 15) | List<Widget> renderItems()
method build (line 30) | Widget build(BuildContext context)
Condensed preview — 61 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (192K chars).
[
{
"path": ".gitignore",
"chars": 1294,
"preview": "# Miscellaneous\n*.class\n*.lock\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*."
},
{
"path": ".metadata",
"chars": 302,
"preview": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrade"
},
{
"path": "README.md",
"chars": 1480,
"preview": "# Flutter Netflix Clone\n\nFlutter App with the same style as Netflix for Android and iOS.\n\n<div style=\"display:flex;flex-"
},
{
"path": "android/app/build.gradle",
"chars": 1874,
"preview": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertie"
},
{
"path": "android/app/src/main/AndroidManifest.xml",
"chars": 1988,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.example.netflixclone\">\n\n <!-- T"
},
{
"path": "android/app/src/main/java/com/example/netflixclone/MainActivity.java",
"chars": 369,
"preview": "package com.example.netflixclone;\n\nimport android.os.Bundle;\nimport io.flutter.app.FlutterActivity;\nimport io.flutter.pl"
},
{
"path": "android/app/src/main/res/drawable/launch_background.xml",
"chars": 369,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmln"
},
{
"path": "android/app/src/main/res/values/styles.xml",
"chars": 361,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTi"
},
{
"path": "android/build.gradle",
"chars": 470,
"preview": "buildscript {\n repositories {\n google()\n jcenter()\n }\n\n dependencies {\n classpath 'com.and"
},
{
"path": "android/gradle/wrapper/gradle-wrapper.properties",
"chars": 234,
"preview": "#Fri Jun 23 08:50:38 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER"
},
{
"path": "android/gradle.properties",
"chars": 29,
"preview": "org.gradle.jvmargs=-Xmx1536M\n"
},
{
"path": "android/settings.gradle",
"chars": 484,
"preview": "include ':app'\n\ndef flutterProjectRoot = rootProject.projectDir.parentFile.toPath()\n\ndef plugins = new Properties()\ndef "
},
{
"path": "ios/Flutter/AppFrameworkInfo.plist",
"chars": 773,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "ios/Flutter/Debug.xcconfig",
"chars": 106,
"preview": "#include \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
},
{
"path": "ios/Flutter/Release.xcconfig",
"chars": 108,
"preview": "#include \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
},
{
"path": "ios/Flutter/flutter_export_environment.sh",
"chars": 512,
"preview": "#!/bin/sh\n# This is a generated file; do not edit or check into version control.\nexport \"FLUTTER_ROOT=/Users/dennysegura"
},
{
"path": "ios/Podfile",
"chars": 2208,
"preview": "# Uncomment this line to define a global platform for your project\n# platform :ios, '9.0'\n\n# CocoaPods analytics sends n"
},
{
"path": "ios/Runner/AppDelegate.h",
"chars": 103,
"preview": "#import <Flutter/Flutter.h>\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : FlutterAppDelegate\n\n@end\n"
},
{
"path": "ios/Runner/AppDelegate.m",
"chars": 424,
"preview": "#include \"AppDelegate.h\"\n#include \"GeneratedPluginRegistrant.h\"\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApp"
},
{
"path": "ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 2519,
"preview": "{\n \"images\" : [\n {\n \"size\" : \"20x20\",\n \"idiom\" : \"iphone\",\n \"filename\" : \"Icon-App-20x20@2x.png\",\n "
},
{
"path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
"chars": 391,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"LaunchImage.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
"chars": 336,
"preview": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in"
},
{
"path": "ios/Runner/Base.lproj/LaunchScreen.storyboard",
"chars": 2377,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "ios/Runner/Base.lproj/Main.storyboard",
"chars": 1605,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "ios/Runner/Info.plist",
"chars": 1511,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "ios/Runner/main.m",
"chars": 226,
"preview": "#import <Flutter/Flutter.h>\n#import <UIKit/UIKit.h>\n#import \"AppDelegate.h\"\n\nint main(int argc, char* argv[]) {\n @autor"
},
{
"path": "ios/Runner.xcodeproj/project.pbxproj",
"chars": 21149,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 152,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:Runner.xcodepr"
},
{
"path": "ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
"chars": 3331,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0910\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "ios/Runner.xcworkspace/contents.xcworkspacedata",
"chars": 152,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:Runner.xcodepr"
},
{
"path": "lib/app.dart",
"chars": 1994,
"preview": "library netflix;\n\n// Dart Imports\nimport 'dart:async';\nimport 'dart:convert';\n\n// Flutter imports\nimport 'package:http/h"
},
{
"path": "lib/main.dart",
"chars": 120,
"preview": "import 'package:flutter/material.dart';\nimport 'package:netflix_clone/app.dart';\n\nvoid main() => runApp(Netflix());\n\n\n\n\n"
},
{
"path": "lib/src/blocs/movies_bloc.dart",
"chars": 672,
"preview": "part of netflix;\n\nclass MoviesBloc {\n final _repository = Repository();\n final _moviesFetcher = PublishSubject<List<It"
},
{
"path": "lib/src/helpers/config/application.dart",
"chars": 64,
"preview": "part of netflix;\n\nclass Application {\n static Router router;\n}\n"
},
{
"path": "lib/src/helpers/config/constants.dart",
"chars": 71042,
"preview": "part of netflix;\n\nMap<String, dynamic> tvShow = {\n \"details\": {\n \"genres\": [\"Drama\", \"Crime\"],\n \"cast\": [\n {"
},
{
"path": "lib/src/helpers/config/route_handlers.dart",
"chars": 915,
"preview": "part of netflix;\n\nvar rootHandler = Handler(\n handlerFunc: (BuildContext context, Map<String, List<String>> params,\n "
},
{
"path": "lib/src/helpers/config/routes.dart",
"chars": 748,
"preview": "part of netflix;\n\nclass Routes {\n static String root = '/';\n static String summary = '/summary';\n static String detai"
},
{
"path": "lib/src/models/episode.dart",
"chars": 861,
"preview": "part of netflix;\n\nclass Episode {\n int _number;\n int _season;\n String _image;\n String _summary;\n String _name;\n in"
},
{
"path": "lib/src/models/item_model.dart",
"chars": 377,
"preview": "part of netflix;\n\nclass ItemModel {\n String _title;\n List<Result> _results = [];\n\n ItemModel.fromJson(Map<String, dyn"
},
{
"path": "lib/src/models/result.dart",
"chars": 1437,
"preview": "part of netflix;\n\nclass Result {\n int id;\n String _name;\n String _image;\n List<String> _genres;\n List<String> _cast"
},
{
"path": "lib/src/pages/detail/index.dart",
"chars": 237,
"preview": "part of netflix;\n\nclass TvShow extends StatefulWidget {\n final int match;\n final Result item;\n\n TvShow({\n Key key,"
},
{
"path": "lib/src/pages/detail/state.dart",
"chars": 19845,
"preview": "part of netflix;\n\nclass TvShowState extends State<TvShow> {\n var currentSeason = 1;\n @override\n void initState() {\n "
},
{
"path": "lib/src/pages/filter/index.dart",
"chars": 239,
"preview": "part of netflix;\n\nclass Filter extends StatefulWidget {\n final String title;\n final String type;\n Filter({\n Key ke"
},
{
"path": "lib/src/pages/filter/state.dart",
"chars": 3260,
"preview": "part of netflix;\n\nclass FilterState extends State<Filter> {\n String filterSelected;\n List<String> options = ['Series',"
},
{
"path": "lib/src/pages/home/index.dart",
"chars": 181,
"preview": "part of netflix;\n\nclass Home extends StatefulWidget {\n Home({Key key, this.title}) : super(key: key);\n final String ti"
},
{
"path": "lib/src/pages/home/state.dart",
"chars": 1249,
"preview": "part of netflix;\n\nclass HomeState extends State<Home> with SingleTickerProviderStateMixin {\n TabController controller;\n"
},
{
"path": "lib/src/pages/summary/index.dart",
"chars": 193,
"preview": "part of netflix;\n\nclass Summary extends StatefulWidget {\n Summary({Key key, this.title}) : super(key: key);\n final Str"
},
{
"path": "lib/src/pages/summary/state.dart",
"chars": 9808,
"preview": "part of netflix;\n\nclass SummaryState extends State<Summary> {\n \n void goTo(String type) {\n Application.router.navig"
},
{
"path": "lib/src/pages/video/index.dart",
"chars": 186,
"preview": "part of netflix;\n\nclass Video extends StatefulWidget {\n final String title;\n Video({Key key, this.title}) : super(key:"
},
{
"path": "lib/src/pages/video/state.dart",
"chars": 1793,
"preview": "part of netflix;\n\nclass VideoState extends State<Video> {\n VideoPlayerController vcontroller;\n bool controlVisible;\n "
},
{
"path": "lib/src/resources/movie_api_provider.dart",
"chars": 718,
"preview": "part of netflix;\n\nclass MovieApiProvider {\n final String host = 'https://your-host';\n Client client = Client();\n\n Fut"
},
{
"path": "lib/src/resources/repository.dart",
"chars": 241,
"preview": "part of netflix;\n\nclass Repository {\n final moviesApiProvider = MovieApiProvider();\n\n Future<List<ItemModel>> fetchAll"
},
{
"path": "lib/src/utils/theme/color.dart",
"chars": 168,
"preview": "part of netflix;\n\nclass Palette {\n static const Color dune = Color.fromRGBO(40, 38, 35, 1.0);\n static const Color yell"
},
{
"path": "lib/src/utils/theme/typography.dart",
"chars": 2105,
"preview": "part of netflix;\n\nclass TextTheme {\n static const TextStyle heading1 = TextStyle(\n fontFamily: 'GoogleSans',\n fon"
},
{
"path": "lib/src/widgets/player-controls/index.dart",
"chars": 314,
"preview": "part of netflix;\n\nclass PlayerControl extends StatefulWidget {\n final VideoPlayerController controller;\n final String "
},
{
"path": "lib/src/widgets/player-controls/state.dart",
"chars": 6134,
"preview": "part of netflix;\n\nclass PlayerControlState extends State<PlayerControl> {\n VideoPlayerController get controller => widg"
},
{
"path": "lib/src/widgets/player-life-cycle/index.dart",
"chars": 415,
"preview": "part of netflix;\n\ntypedef Widget VideoWidgetBuilder(\n BuildContext context, VideoPlayerController controller);\n\nclass"
},
{
"path": "lib/src/widgets/player-life-cycle/state.dart",
"chars": 597,
"preview": "part of netflix;\n\nclass PlayerLifeCycleState extends State<PlayerLifeCycle> {\n VideoPlayerController get controller => "
},
{
"path": "lib/src/widgets/tvshow-list/index.dart",
"chars": 1274,
"preview": "part of netflix;\n\nclass ShowsList extends StatelessWidget {\n final ScrollController controller = ScrollController();\n "
},
{
"path": "pubspec.yaml",
"chars": 1377,
"preview": "name: netflix_clone\ndescription: A new Flutter project.\nversion: 1.0.0+1\n\nenvironment:\n sdk: \">=2.0.0-dev.68.0 <3.0.0\"\n"
},
{
"path": "test/widget_test.dart",
"chars": 1124,
"preview": "// // This is a basic Flutter widget test.\n// //\n// // To perform an interaction with a widget in your test, use the Wid"
}
]
About this extraction
This page contains the full source code of the devdennysegura/flutter-netflix-clone GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 61 files (172.8 KB), approximately 45.3k tokens, and a symbol index with 73 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.