├── supabase
├── seed.sql
├── .gitignore
├── .env.example
├── migrations
│ ├── 20231010044029_related_films_function.sql
│ └── 20231009120230_films_table.sql
├── functions
│ └── get_film_data
│ │ └── index.ts
└── config.toml
├── flutter
├── ios
│ ├── Runner
│ │ ├── Runner-Bridging-Header.h
│ │ ├── Assets.xcassets
│ │ │ ├── LaunchImage.imageset
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ ├── README.md
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ ├── Base.lproj
│ │ │ ├── Main.storyboard
│ │ │ └── LaunchScreen.storyboard
│ │ └── Info.plist
│ ├── Flutter
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── AppFrameworkInfo.plist
│ ├── Runner.xcodeproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ ├── xcshareddata
│ │ │ └── xcschemes
│ │ │ │ └── Runner.xcscheme
│ │ └── project.pbxproj
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── RunnerTests
│ │ └── RunnerTests.swift
│ ├── .gitignore
│ ├── Podfile
│ └── Podfile.lock
├── web
│ ├── favicon.png
│ ├── icons
│ │ ├── Icon-192.png
│ │ ├── Icon-512.png
│ │ ├── Icon-maskable-192.png
│ │ └── Icon-maskable-512.png
│ ├── manifest.json
│ └── index.html
├── android
│ ├── gradle.properties
│ ├── app
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── drawable
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── drawable-v21
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── values
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ └── values-night
│ │ │ │ │ │ └── styles.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ │ └── supabase
│ │ │ │ │ │ └── filmsearch
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── .gitignore
│ ├── build.gradle
│ └── settings.gradle
├── pubspec.yaml
├── lib
│ ├── models
│ │ └── film.dart
│ ├── main.dart
│ ├── pages
│ │ ├── home_page.dart
│ │ └── details_page.dart
│ └── components
│ │ └── film_cell.dart
├── .gitignore
├── test
│ └── widget_test.dart
├── analysis_options.yaml
├── .metadata
└── pubspec.lock
├── .github
├── images
│ └── app.jpg
└── workflows
│ └── ci.yaml
├── .vscode
└── settings.json
├── filmsearch.code-workspace
└── README.md
/supabase/seed.sql:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/supabase/.gitignore:
--------------------------------------------------------------------------------
1 | # Supabase
2 | .branches
3 | .temp
4 | .env
--------------------------------------------------------------------------------
/flutter/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/supabase/.env.example:
--------------------------------------------------------------------------------
1 | TMDB_API_KEY=your_tmdb_api_key
2 | OPEN_AI_API_KEY=your_tmdb_api_key
--------------------------------------------------------------------------------
/.github/images/app.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/.github/images/app.jpg
--------------------------------------------------------------------------------
/flutter/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/web/favicon.png
--------------------------------------------------------------------------------
/flutter/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/flutter/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/flutter/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "deno.enable": true,
3 | "deno.lint": true,
4 | "deno.unstable": true,
5 | "deno.cacheOnSave": true
6 | }
7 |
--------------------------------------------------------------------------------
/flutter/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/flutter/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/flutter/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/flutter/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/flutter/android/app/src/main/kotlin/com/supabase/filmsearch/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.supabase.filmsearch
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshukertjr/flutter-movie-recommendation/HEAD/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/flutter/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/flutter/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
6 |
--------------------------------------------------------------------------------
/flutter/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/flutter/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flutter/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/filmsearch.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "name": "project-root",
5 | "path": "./"
6 | },
7 | {
8 | "name": "supabase-functions",
9 | "path": "supabase/functions"
10 | }
11 | ],
12 | "settings": {
13 | "files.exclude": {
14 | "supabase/functions/": true
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/flutter/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/flutter/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/flutter/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/flutter/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/flutter/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: filmsearch
2 | description: A new Flutter project.
3 |
4 | publish_to: 'none'
5 |
6 | version: 1.0.0+1
7 |
8 | environment:
9 | sdk: '>=3.1.3 <4.0.0'
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 |
15 | supabase_flutter: ^1.10.22
16 | intl: ^0.18.1
17 |
18 | dev_dependencies:
19 | flutter_test:
20 | sdk: flutter
21 |
22 | flutter_lints: ^2.0.0
23 |
24 | flutter:
25 | uses-material-design: true
26 |
--------------------------------------------------------------------------------
/flutter/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/supabase/migrations/20231010044029_related_films_function.sql:
--------------------------------------------------------------------------------
1 | -- Set index on embedding column
2 | create index on films using hnsw (embedding vector_cosine_ops);
3 |
4 | -- Create function to find related films
5 | create or replace function get_related_film(embedding vector(1536), film_id integer)
6 | returns setof films
7 | language sql
8 | as $$
9 | select *
10 | from films
11 | where id != film_id
12 | order by films.embedding <=> get_related_film.embedding
13 | limit 6;
14 | $$ security invoker;
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/supabase/migrations/20231009120230_films_table.sql:
--------------------------------------------------------------------------------
1 | -- Enable pgvector extension
2 | create extension vector
3 | with
4 | schema extensions;
5 |
6 | -- Create table
7 | create table public.films (
8 | id integer primary key,
9 | title text,
10 | overview text,
11 | release_date date,
12 | backdrop_path text,
13 | embedding vector(1536)
14 | );
15 |
16 | -- Enable row level security
17 | alter table public.films enable row level security;
18 |
19 | -- Create policy to allow anyone to read the films table
20 | create policy "Fils are public." on public.films for select using (true);
21 |
--------------------------------------------------------------------------------
/flutter/lib/models/film.dart:
--------------------------------------------------------------------------------
1 | class Film {
2 | final int id;
3 | final String title;
4 | final String overview;
5 | final String imageUrl;
6 | final DateTime releaseDate;
7 | final String embedding;
8 |
9 | Film.fromJson(Map json)
10 | : id = json['id'] as int,
11 | title = json['title'] as String,
12 | overview = json['overview'] as String,
13 | imageUrl =
14 | 'https://image.tmdb.org/t/p/w500${json['backdrop_path'] as String}',
15 | releaseDate = DateTime.parse(json['release_date'] as String),
16 | embedding = json['embedding'] as String;
17 | }
18 |
--------------------------------------------------------------------------------
/flutter/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: Deploy Migrations to Production
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | workflow_dispatch:
8 |
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-latest
12 |
13 | env:
14 | SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
15 | SUPABASE_DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
16 | SUPABASE_PROJECT_ID: ${{ secrets.PROJECT_ID }}
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 |
21 | - uses: supabase/setup-cli@v1
22 | with:
23 | version: latest
24 |
25 | - run: supabase link --project-ref $SUPABASE_PROJECT_ID
26 | - run: supabase db push
27 | - run: supabase functions deploy --project-ref $SUPABASE_PROJECT_ID
28 |
--------------------------------------------------------------------------------
/flutter/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.3.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | tasks.register("clean", Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/flutter/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }
9 | settings.ext.flutterSdkPath = flutterSdkPath()
10 |
11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
12 |
13 | plugins {
14 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
15 | }
16 | }
17 |
18 | include ":app"
19 |
20 | apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
21 |
--------------------------------------------------------------------------------
/flutter/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
--------------------------------------------------------------------------------
/flutter/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/flutter/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "filmsearch",
3 | "short_name": "filmsearch",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/flutter/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:filmsearch/pages/home_page.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:supabase_flutter/supabase_flutter.dart';
4 |
5 | void main() async {
6 | await Supabase.initialize(
7 | url: 'https://dqbvrguqoahxeqraccxo.supabase.co',
8 | anonKey:
9 | 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRxYnZyZ3Vxb2FoeGVxcmFjY3hvIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTY4NTQ0MDgsImV4cCI6MjAxMjQzMDQwOH0.8GyUQfmINFOrBRuN8OTbocb3U4T3LSp-1Z6C0W1LOJ0',
10 | );
11 | runApp(const MyApp());
12 | }
13 |
14 | final supabase = Supabase.instance.client;
15 |
16 | class MyApp extends StatelessWidget {
17 | const MyApp({super.key});
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return MaterialApp(
22 | title: 'Flutter Demo',
23 | debugShowCheckedModeBanner: false,
24 | theme: ThemeData(
25 | colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
26 | useMaterial3: true,
27 | ),
28 | home: const HomePage(),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/flutter/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/flutter/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility in the flutter_test package. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:filmsearch/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(const MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/flutter/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at https://dart.dev/lints.
17 | #
18 | # Instead of disabling a lint rule for the entire project in the
19 | # section below, it can also be suppressed for a single line of code
20 | # or a specific dart file by using the `// ignore: name_of_lint` and
21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
22 | # producing the lint.
23 | rules:
24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
26 |
27 | # Additional information about this file can be found at
28 | # https://dart.dev/guides/language/analysis-options
29 |
--------------------------------------------------------------------------------
/flutter/lib/pages/home_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:filmsearch/components/film_cell.dart';
2 | import 'package:filmsearch/main.dart';
3 | import 'package:filmsearch/models/film.dart';
4 |
5 | import 'package:flutter/material.dart';
6 |
7 | class HomePage extends StatefulWidget {
8 | const HomePage({super.key});
9 |
10 | @override
11 | State createState() => _HomePageState();
12 | }
13 |
14 | class _HomePageState extends State {
15 | final filmsFuture = supabase
16 | .from('films')
17 | .select>>()
18 | .withConverter>((data) => data.map(Film.fromJson).toList());
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return Scaffold(
23 | appBar: AppBar(
24 | title: const Text('Films'),
25 | ),
26 | body: FutureBuilder(
27 | future: filmsFuture,
28 | builder: (context, snapshot) {
29 | if (snapshot.hasError) {
30 | return Center(
31 | child: Text(snapshot.error.toString()),
32 | );
33 | }
34 | if (!snapshot.hasData) {
35 | return const Center(child: CircularProgressIndicator());
36 | }
37 | final films = snapshot.data!;
38 | return ListView.builder(
39 | itemBuilder: (context, index) {
40 | final film = films[index];
41 | return FilmCell(film: film);
42 | },
43 | itemCount: films.length,
44 | );
45 | }),
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/flutter/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '11.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | target 'RunnerTests' do
36 | inherit! :search_paths
37 | end
38 | end
39 |
40 | post_install do |installer|
41 | installer.pods_project.targets.each do |target|
42 | flutter_additional_ios_build_settings(target)
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/flutter/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/flutter/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
14 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/flutter/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Filmsearch
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | filmsearch
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | CADisableMinimumFrameDurationOnPhone
45 |
46 | UIApplicationSupportsIndirectInputEvents
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/flutter/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "ead455963c12b453cdb2358cad34969c76daf180"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: ead455963c12b453cdb2358cad34969c76daf180
17 | base_revision: ead455963c12b453cdb2358cad34969c76daf180
18 | - platform: android
19 | create_revision: ead455963c12b453cdb2358cad34969c76daf180
20 | base_revision: ead455963c12b453cdb2358cad34969c76daf180
21 | - platform: ios
22 | create_revision: ead455963c12b453cdb2358cad34969c76daf180
23 | base_revision: ead455963c12b453cdb2358cad34969c76daf180
24 | - platform: linux
25 | create_revision: ead455963c12b453cdb2358cad34969c76daf180
26 | base_revision: ead455963c12b453cdb2358cad34969c76daf180
27 | - platform: macos
28 | create_revision: ead455963c12b453cdb2358cad34969c76daf180
29 | base_revision: ead455963c12b453cdb2358cad34969c76daf180
30 | - platform: web
31 | create_revision: ead455963c12b453cdb2358cad34969c76daf180
32 | base_revision: ead455963c12b453cdb2358cad34969c76daf180
33 | - platform: windows
34 | create_revision: ead455963c12b453cdb2358cad34969c76daf180
35 | base_revision: ead455963c12b453cdb2358cad34969c76daf180
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > [!WARNING]
2 | > This repo has been moved [here](https://github.com/dshukertjr/examples/tree/main/movie-recommendation).
3 |
4 | # Content recommendation feature using Flutter, Open AI and Supabase
5 |
6 | A Flutter app demonstrating how semantic search powered by Open AI and Supabase vector database can be used to build a recommendation engine for movie films.
7 |
8 | 
9 |
10 | ## Getting Started
11 |
12 | Obtain environment variables
13 | Head to [TMDB API](https://developer.themoviedb.org/reference/intro/getting-started), and [Open AI API](https://openai.com/blog/openai-api) to create an API key. Then copy `supabase/.env.example` to `supabase/.env` and fill in the variables.
14 |
15 | ```bash
16 | TMDB_API_KEY=your_tmdb_api_key
17 | OPEN_AI_API_KEY=your_tmdb_api_key
18 | ```
19 |
20 | Set environment variables on Supabase Edge functions
21 |
22 | ```bash
23 | supabase link --project-ref YOUR_PROJECT_REF
24 | supabase secrets set --env-file ./supabase/.env
25 | ```
26 |
27 | Install the Flutter dependencies:
28 |
29 | ```bash
30 | cd flutter
31 | dart pub get
32 | cd ..
33 | ```
34 |
35 | Setup Supabase project
36 |
37 | ```bash
38 | supabase link --project-ref YOUR_PROJECT_REF
39 | supabase db push
40 | ```
41 |
42 | Deploy edge functions
43 |
44 | ```bash
45 | supabase functions deploy
46 | ```
47 |
48 | Run the Flutter app
49 |
50 | ```bash
51 | flutter run
52 | ```
53 |
54 | ## Tools used
55 |
56 | - [Flutter](https://flutter.dev/) - Used to create the interface of the app
57 | - [Supabase](https://supabase.com/) - Used to store embeddings as well as other movie data in the database
58 | - [Open AI API](https://openai.com/blog/openai-api) - Used to convert movie data into embeddings
59 | - [TMDB API](https://developer.themoviedb.org/docs) - Used to retrieve movie data
60 |
--------------------------------------------------------------------------------
/flutter/lib/components/film_cell.dart:
--------------------------------------------------------------------------------
1 | import 'package:filmsearch/models/film.dart';
2 | import 'package:filmsearch/pages/details_page.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class FilmCell extends StatelessWidget {
6 | const FilmCell({
7 | super.key,
8 | required this.film,
9 | this.fontSize = 20,
10 | this.isHeroEnabled = true,
11 | });
12 | final Film film;
13 | final double fontSize;
14 | final bool isHeroEnabled;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return InkWell(
19 | onTap: () {
20 | Navigator.of(context).push(
21 | MaterialPageRoute(
22 | builder: (context) => DetailsPage(film: film),
23 | ),
24 | );
25 | },
26 | child: Stack(
27 | children: [
28 | AspectRatio(
29 | aspectRatio: 16 / 9,
30 | child: HeroMode(
31 | enabled: isHeroEnabled,
32 | child: Hero(
33 | tag: film.imageUrl,
34 | child: Image.network(film.imageUrl),
35 | ),
36 | ),
37 | ),
38 | Positioned.fill(
39 | top: null,
40 | child: DecoratedBox(
41 | decoration: BoxDecoration(
42 | gradient: LinearGradient(
43 | begin: Alignment.bottomCenter,
44 | end: Alignment.topCenter,
45 | colors: [
46 | Colors.black,
47 | Colors.black.withAlpha(0),
48 | ],
49 | )),
50 | child: Padding(
51 | padding: const EdgeInsets.all(8.0),
52 | child: Text(
53 | film.title,
54 | style: TextStyle(
55 | color: Colors.white,
56 | fontSize: fontSize,
57 | ),
58 | ),
59 | ),
60 | ),
61 | ),
62 | ],
63 | ),
64 | );
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/flutter/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | filmsearch
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/flutter/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - app_links (0.0.1):
3 | - Flutter
4 | - Flutter (1.0.0)
5 | - path_provider_foundation (0.0.1):
6 | - Flutter
7 | - FlutterMacOS
8 | - shared_preferences_foundation (0.0.1):
9 | - Flutter
10 | - FlutterMacOS
11 | - sign_in_with_apple (0.0.1):
12 | - Flutter
13 | - url_launcher_ios (0.0.1):
14 | - Flutter
15 | - webview_flutter_wkwebview (0.0.1):
16 | - Flutter
17 |
18 | DEPENDENCIES:
19 | - app_links (from `.symlinks/plugins/app_links/ios`)
20 | - Flutter (from `Flutter`)
21 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
22 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
23 | - sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
24 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
25 | - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
26 |
27 | EXTERNAL SOURCES:
28 | app_links:
29 | :path: ".symlinks/plugins/app_links/ios"
30 | Flutter:
31 | :path: Flutter
32 | path_provider_foundation:
33 | :path: ".symlinks/plugins/path_provider_foundation/darwin"
34 | shared_preferences_foundation:
35 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
36 | sign_in_with_apple:
37 | :path: ".symlinks/plugins/sign_in_with_apple/ios"
38 | url_launcher_ios:
39 | :path: ".symlinks/plugins/url_launcher_ios/ios"
40 | webview_flutter_wkwebview:
41 | :path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
42 |
43 | SPEC CHECKSUMS:
44 | app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
45 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
46 | path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
47 | shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
48 | sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
49 | url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
50 | webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
51 |
52 | PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189
53 |
54 | COCOAPODS: 1.14.3
55 |
--------------------------------------------------------------------------------
/flutter/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | def localProperties = new Properties()
8 | def localPropertiesFile = rootProject.file('local.properties')
9 | if (localPropertiesFile.exists()) {
10 | localPropertiesFile.withReader('UTF-8') { reader ->
11 | localProperties.load(reader)
12 | }
13 | }
14 |
15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
16 | if (flutterVersionCode == null) {
17 | flutterVersionCode = '1'
18 | }
19 |
20 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
21 | if (flutterVersionName == null) {
22 | flutterVersionName = '1.0'
23 | }
24 |
25 | android {
26 | namespace "com.supabase.filmsearch"
27 | compileSdkVersion flutter.compileSdkVersion
28 | ndkVersion flutter.ndkVersion
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 |
43 | defaultConfig {
44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
45 | applicationId "com.supabase.filmsearch"
46 | // You can update the following values to match your application needs.
47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
48 | minSdkVersion flutter.minSdkVersion
49 | targetSdkVersion flutter.targetSdkVersion
50 | versionCode flutterVersionCode.toInteger()
51 | versionName flutterVersionName
52 | }
53 |
54 | buildTypes {
55 | release {
56 | // TODO: Add your own signing config for the release build.
57 | // Signing with the debug keys for now, so `flutter run --release` works.
58 | signingConfig signingConfigs.debug
59 | }
60 | }
61 | }
62 |
63 | flutter {
64 | source '../..'
65 | }
66 |
67 | dependencies {}
68 |
--------------------------------------------------------------------------------
/flutter/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/flutter/lib/pages/details_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:filmsearch/components/film_cell.dart';
2 | import 'package:filmsearch/main.dart';
3 | import 'package:filmsearch/models/film.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:intl/intl.dart';
6 |
7 | class DetailsPage extends StatefulWidget {
8 | const DetailsPage({super.key, required this.film});
9 |
10 | final Film film;
11 |
12 | @override
13 | State createState() => _DetailsPageState();
14 | }
15 |
16 | class _DetailsPageState extends State {
17 | late final Future> relatedFilmsFuture;
18 |
19 | @override
20 | void initState() {
21 | super.initState();
22 | relatedFilmsFuture = supabase.rpc('get_related_film', params: {
23 | 'embedding': widget.film.embedding,
24 | 'film_id': widget.film.id,
25 | }).withConverter>((data) =>
26 | List