CINXE.COM
Building a fullstack movie recommendation system | Google Codelabs
<!doctype html> <html lang="en" dir="ltr"> <head> <meta name="google-signin-client-id" content="721724668570-nbkv1cfusk7kk4eni4pjvepaus73b13t.apps.googleusercontent.com"> <meta name="google-signin-scope" content="profile email https://www.googleapis.com/auth/developerprofiles https://www.googleapis.com/auth/developerprofiles.award"> <meta property="og:site_name" content="Google Codelabs"> <meta property="og:type" content="website"><meta name="theme-color" content="#1a73e8"><meta charset="utf-8"> <meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="manifest" href="/_pwa/codelabs/manifest.json" crossorigin="use-credentials"> <link rel="preconnect" href="//www.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.googleapis.com" crossorigin> <link rel="preconnect" href="//apis.google.com" crossorigin> <link rel="preconnect" href="//www.google-analytics.com" crossorigin><link rel="stylesheet" href="//fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap"> <link rel="stylesheet" href="//fonts.googleapis.com/css2?family=Material+Icons&family=Material+Symbols+Outlined&display=block"><link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/css/app.css"> <link rel="shortcut icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/images/favicon.png"> <link rel="apple-touch-icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/images/touchicon-180.png"><link rel="canonical" href="https://codelabs.developers.google.com/tfrecommenders-flutter"><link rel="search" type="application/opensearchdescription+xml" title="Google Codelabs" href="https://codelabs.developers.google.com/s/opensearch.xml"> <link rel="alternate" hreflang="en" href="https://codelabs.developers.google.com/tfrecommenders-flutter" /><link rel="alternate" hreflang="x-default" href="https://codelabs.developers.google.com/tfrecommenders-flutter" /><link rel="alternate" hreflang="ar" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=ar" /><link rel="alternate" hreflang="bn" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=bn" /><link rel="alternate" hreflang="zh-Hans" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=zh-cn" /><link rel="alternate" hreflang="zh-Hant" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=zh-tw" /><link rel="alternate" hreflang="fa" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=fa" /><link rel="alternate" hreflang="fr" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=fr" /><link rel="alternate" hreflang="de" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=de" /><link rel="alternate" hreflang="he" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=he" /><link rel="alternate" hreflang="hi" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=hi" /><link rel="alternate" hreflang="id" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=id" /><link rel="alternate" hreflang="it" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=it" /><link rel="alternate" hreflang="ja" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=ja" /><link rel="alternate" hreflang="ko" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=ko" /><link rel="alternate" hreflang="pl" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=pl" /><link rel="alternate" hreflang="pt-BR" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=pt-br" /><link rel="alternate" hreflang="ru" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=ru" /><link rel="alternate" hreflang="es-419" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=es-419" /><link rel="alternate" hreflang="th" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=th" /><link rel="alternate" hreflang="tr" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=tr" /><link rel="alternate" hreflang="vi" href="https://codelabs.developers.google.com/tfrecommenders-flutter?hl=vi" /><title>Building a fullstack movie recommendation system | Google Codelabs</title> <meta property="og:title" content="Building a fullstack movie recommendation system | Google Codelabs"><meta name="description" content="In this codelab, you’ll build a fullstack recommendation system. You will use TensorFlow Recommenders to train 2 recommendation models and deploy them using TensorFlow Serving as the backend. You will also build a cross-platform Flutter app as the frontend."> <meta property="og:description" content="In this codelab, you’ll build a fullstack recommendation system. You will use TensorFlow Recommenders to train 2 recommendation models and deploy them using TensorFlow Serving as the backend. You will also build a cross-platform Flutter app as the frontend."><meta property="og:url" content="https://codelabs.developers.google.com/tfrecommenders-flutter"><meta property="og:locale" content="en"> <link rel="stylesheet" href="/extras.css"></head> <body class="" template="codelab" theme="codelabs-theme" type="codelab" layout="docs" display-toc pending> <devsite-progress type="indeterminate" id="app-progress"></devsite-progress> <section class="devsite-wrapper"> <devsite-cookie-notification-bar></devsite-cookie-notification-bar><devsite-header role="banner"> <div class="devsite-header--inner nocontent"> <div class="devsite-top-logo-row-wrapper-wrapper"> <div class="devsite-top-logo-row-wrapper"> <div class="devsite-top-logo-row"> <button type="button" id="devsite-hamburger-menu" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Navigation menu button" visually-hidden aria-label="Open menu"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="googleCodelabs" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/images/lockup.svg" class="devsite-site-logo" alt="Google Codelabs"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> <div class="devsite-top-logo-row-middle"> <div class="devsite-header-upper-tabs"> </div> <devsite-search enable-signin enable-search enable-suggestions enable-query-completion project-name="Codelabs" tenant-name="Google Codelabs" > <form class="devsite-search-form" action="https://codelabs.developers.google.com/s/results" method="GET"> <div class="devsite-search-container"> <button type="button" search-open class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Open search"></button> <div class="devsite-searchbox"> <input aria-activedescendant="" aria-autocomplete="list" aria-label="Search" aria-expanded="false" aria-haspopup="listbox" autocomplete="off" class="devsite-search-field devsite-search-query" name="q" placeholder="Search" role="combobox" type="text" value="" > <div class="devsite-search-image material-icons" aria-hidden="true"> </div> <div class="devsite-search-shortcut-icon-container" aria-hidden="true"> <kbd class="devsite-search-shortcut-icon">/</kbd> </div> </div> </div> </form> <button type="button" search-close class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Close search"></button> </devsite-search> </div> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es" >Español</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> <devsite-user enable-profiles fp-auth id="devsite-user"> <span class="button devsite-top-button" aria-hidden="true" visually-hidden>Sign in</span> </devsite-user> </div> </div> </div> <div class="devsite-collapsible-section devsite-header-no-lower-tabs "> <div class="devsite-header-background"> </div> </div> </div> </devsite-header> <devsite-book-nav scrollbars hidden> <div class="devsite-book-nav-filter" hidden> <span class="filter-list-icon material-icons" aria-hidden="true"></span> <input type="text" placeholder="Filter" aria-label="Type to filter" role="searchbox"> <span class="filter-clear-button hidden" data-title="Clear filter" aria-label="Clear filter" role="button" tabindex="0"></span> </div> <nav class="devsite-book-nav devsite-nav nocontent" aria-label="Side menu"> <div class="devsite-mobile-header"> <button type="button" id="devsite-close-nav" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Close navigation" aria-label="Close navigation"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="googleCodelabs" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/images/lockup.svg" class="devsite-site-logo" alt="Google Codelabs"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> </div> <div class="devsite-book-nav-wrapper"> <div class="devsite-mobile-nav-top"> <ul class="devsite-nav-list"> </ul> </div> </div> </nav> </devsite-book-nav> <section id="gc-wrapper"> <main role="main" class="devsite-main-content" has-sidebar > <div class="devsite-sidebar"> <div class="devsite-sidebar-content"> <devsite-toc class="devsite-nav" role="navigation" aria-label="On this page" depth="1" scrollbars ></devsite-toc> <devsite-recommendations-sidebar class="nocontent devsite-nav"> </devsite-recommendations-sidebar> </div> </div> <devsite-content> <article class="devsite-article"><style> body { transition: opacity ease-in 0.2s; } body[unresolved] { opacity: 0; display: block; overflow: hidden; position: relative; } </style> <div class="devsite-article-meta nocontent" role="navigation"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> <a href="https://codelabs.developers.google.com/" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="Codelabs" > Codelabs </a> </li> </ul> </div> <h1 class="devsite-page-title" tabindex="-1"> Building a fullstack movie recommendation system </h1> <devsite-toc class="devsite-nav" depth="1" devsite-toc-embedded > </devsite-toc> <div class="devsite-article-body clearfix "> <google-codelab-analytics gaid="UA-49880327-14" ga4id="G-JTFZSJVVVZ"></google-codelab-analytics> <google-codelab codelab-gaid="UA-52746336-1" codelab-ga4id="" doc-id="1h1kvL9EfeK5f7L7c_p3lRoxEAFRKh0n3za_uzi6XjWI" id="tfrecommenders-flutter" title="Building a fullstack movie recommendation system" no-tooltip="" environment="web" category="" feedback-link="Default" layout="paginated" > <google-codelab-step label="Before you begin" duration="3" step="0"> <google-codelab-about codelab-title="Building a fullstack movie recommendation system" authors="Wei Wei" last-updated="2024-01-21T22:38:51Z" duration="59"> </google-codelab-about> <h2 class="step-title" id="0" data-text="Before you begin" tabindex="-1"> 1. Before you begin </h2> <p>From recommending movies or restaurants, to highlighting entertaining videos, recommendation engines, also known as recommenders, are a very important application of machine learning. Recommenders help you surface compelling content from a large pool of candidates to your users. For example, the Google Play Store offers millions of apps to install, while YouTube provides billions of videos to watch. And even more apps and videos are added every day.</p> <p>In this codelab, you learn how to build a fullstack recommender using:</p> <ul> <li>TensorFlow Recommenders to train a retrieval and a ranking model for movie recommendations</li> <li>TensorFlow Serving to serve the models</li> <li>Flutter to create a cross-platform app to display recommended movies</li> </ul> <h2 is-upgraded id="prerequisites" data-text="Prerequisites" tabindex="-1">Prerequisites</h2> <ul> <li>Basic knowledge of Flutter development with Dart</li> <li>Basic knowledge of machine learning with TensorFlow, such as training versus deployment</li> <li>Basic familiarity with recommendation systems</li> <li>Basic knowledge of Python, terminals and Docker</li> </ul> <h2 class="checklist" is-upgraded id="what-youll-learn" data-text="What you'll learn" tabindex="-1">What you'll learn</h2> <ul class="checklist"> <li>How to train retrieval and ranking models using TensorFlow Recommenders</li> <li>How to serve the trained recommendation models using TensorFlow Serving</li> <li>How to build a cross-platform Flutter app to display the recommended items</li> </ul> <h2 is-upgraded id="what-youll-need" data-text="What you'll need" tabindex="-1">What you'll need</h2> <ul> <li><a href="https://docs.flutter.dev/get-started/install" target="_blank">Flutter SDK</a></li> <li><a href="https://docs.flutter.dev/get-started/install/windows#android-setup" target="_blank">Android</a> and <a href="https://docs.flutter.dev/get-started/install/macos#ios-setup" target="_blank">iOS</a> setup for Flutter</li> <li><a href="https://docs.flutter.dev/desktop" target="_blank">Desktop</a> setup for Flutter</li> <li><a href="https://docs.flutter.dev/get-started/web" target="_blank">Web</a> setup for Flutter</li> <li><a href="https://docs.flutter.dev/get-started/editor?tab=vscode" target="_blank">Visual Studio Code (VS Code) setup for Flutter and Dart</a></li> <li><a href="https://www.docker.com/get-started" target="_blank">Docker</a></li> <li><a href="https://www.gnu.org/software/bash/" target="_blank">Bash</a></li> <li>Python 3.7+</li> <li>Access to <a href="https://colab.research.google.com/" target="_blank">Colab</a></li> </ul> </google-codelab-step> <google-codelab-step label="Set up your Flutter development environment" duration="10" step="1"> <h2 class="step-title" id="1" data-text="Set up your Flutter development environment" tabindex="-1"> 2. Set up your Flutter development environment </h2> <p>For Flutter development, you need two pieces of software to complete this codelab—the <a href="https://docs.flutter.dev/get-started/install" target="_blank">Flutter SDK</a> and <a href="https://docs.flutter.dev/get-started/editor" target="_blank">an editor</a>.</p> <p>You can run the frontend of the codelab using any of these devices:</p> <ul> <li>The <a href="https://docs.flutter.dev/get-started/install/macos#set-up-the-ios-simulator" target="_blank"> iOS simulator</a> (requires installing Xcode tools).</li> <li>The <a href="https://docs.flutter.dev/get-started/install/macos#set-up-the-android-emulator" target="_blank">Android Emulator</a> (requires setup in Android Studio).</li> <li>A browser (Chrome is required for debugging).</li> <li>As a <a href="https://docs.flutter.dev/get-started/install/windows#windows-setup" target="_blank">Windows</a>, <a href="https://docs.flutter.dev/get-started/install/linux#linux-setup" target="_blank">Linux</a>, or <a href="https://docs.flutter.dev/get-started/install/macos#macos-setup" target="_blank">macOS</a> desktop application. You must develop on the platform where you plan to deploy. So, if you want to develop a Windows desktop app, you must develop on Windows to access the appropriate build chain. There are operating system-specific requirements that are covered in detail on <a href="https://docs.flutter.dev/desktop" target="_blank">docs.flutter.dev/desktop</a>.</li> </ul> <p>For the backend, you will need:</p> <ul> <li>A Linux machine or an Intel-based Mac.</li> </ul> </google-codelab-step> <google-codelab-step label="Get set up" duration="2" step="2"> <h2 class="step-title" id="2" data-text="Get set up" tabindex="-1"> 3. Get set up </h2> <p>To download the code for this codelab:</p> <ol type="1"> <li>Navigate to <a href="https://github.com/flutter/codelabs" target="_blank">the GitHub repository</a> for this codelab.</li> <li>Click <strong>Code > Download zip</strong> to download all the code for this codelab.</li> </ol> <p class="image-container"><img alt="2cd45599f51fb8a2.png" style="width: 310.00px" src="/static/tfrecommenders-flutter/img/2cd45599f51fb8a2.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2cd45599f51fb8a2_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> <ol type="1" start="3"> <li>Unzip the downloaded zip file to unpack a <code translate="no" dir="ltr">codelabs-main</code> root folder with all the resources that you need.</li> </ol> <p>For this codelab, you only need the files in the <code translate="no" dir="ltr">tfrs-flutter/</code> subdirectory in the repository, which contains multiple folders:</p> <ul> <li>The <code translate="no" dir="ltr">step0</code> to <code translate="no" dir="ltr">step5</code> folders contain the starter code that you build upon for each step in this codelab.</li> <li>The <code translate="no" dir="ltr">finished</code> folder contains the completed code for the finished sample app.</li> <li>Each folder contains a <code translate="no" dir="ltr">backend</code> subfolder, which includes the recommendation engine backend code, and a <code translate="no" dir="ltr">frontend</code> subfolder, which includes the Flutter frontend code</li> </ul> </google-codelab-step> <google-codelab-step label="Download the dependencies for the project" duration="3" step="3"> <h2 class="step-title" id="3" data-text="Download the dependencies for the project" tabindex="-1"> 4. Download the dependencies for the project </h2> <h2 is-upgraded id="backend" data-text="Backend" tabindex="-1">Backend</h2> <p>We are going to use <a href="https://flask.palletsprojects.com/en/2.1.x/" target="_blank">Flask</a> to create our backend. Open your terminal and run the following:</p> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>pip install Flask flask-cors requests numpy </pre></devsite-code> <h2 is-upgraded id="frontend" data-text="Frontend" tabindex="-1">Frontend</h2> <ol type="1"> <li>In VS Code, click <strong>File > Open folder</strong> and then select the <strong><code translate="no" dir="ltr">step0</code></strong> folder from the source code that you downloaded earlier.</li> <li>Open <code translate="no" dir="ltr">step0/frontend/lib/main.dart</code> file. If you see a VS Code dialog appear that prompts you to download the required packages for the starter app, click <strong>Get packages</strong>.</li> <li>If you don't see this dialog, open your terminal and then run <code translate="no" dir="ltr">flutter pub get</code> command in the <code translate="no" dir="ltr">step0/frontend</code> folder.</li> </ol> <p class="image-container"><img alt="7ada07c300f166a6.png" style="width: 443.00px" src="/static/tfrecommenders-flutter/img/7ada07c300f166a6.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7ada07c300f166a6_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> </google-codelab-step> <google-codelab-step label="Step 0: Run the starter app" duration="3" step="4"> <h2 class="step-title" id="4" data-text="Step 0: Run the starter app" tabindex="-1"> 5. Step 0: Run the starter app </h2> <ol type="1"> <li>Open <code translate="no" dir="ltr">step0/frontend/lib/main.dart</code> file in VS Code, ensure that the Android Emulator or iOS Simulator is properly set up and appears in the status bar.</li> </ol> <p>For example, here's what you see when you use Pixel 5 with the Android Emulator:</p> <p class="image-container"><img alt="9767649231898791.png" style="width: 241.50px" src="/static/tfrecommenders-flutter/img/9767649231898791.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9767649231898791_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> <p>Here's what you see when you use iPhone 13 with the iOS Simulator:</p> <p class="image-container"><img alt="95529e3a682268b2.png" style="width: 151.50px" src="/static/tfrecommenders-flutter/img/95529e3a682268b2.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/95529e3a682268b2_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> <ol type="1" start="2"> <li>Click <img alt="a19a0c68bc4046e6.png" style="width: 25.00px" src="/static/tfrecommenders-flutter/img/a19a0c68bc4046e6.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <strong>Start debugging</strong>.</li> </ol> <aside class="special"><p><strong>Note:</strong> This codelab assumes that you deploy the recommendation models on your local computer. If you deploy the models on another remote machine, change the <code translate="no" dir="ltr">10.0.2.2</code> or <code translate="no" dir="ltr">127.0.0.1</code> IP address in the code to that of the remote machine, which runs TensorFlow Serving.</p> </aside> <h2 is-upgraded id="run-and-explore-the-app" data-text="Run and explore the app" tabindex="-1">Run and explore the app</h2> <p>The app should launch on your Android Emulator or iOS Simulator. The UI is pretty straightforward. There's a text field that lets the user type in the text as the user ID. The Flutter app will send the query request to the backend, which runs 2 recommendation models and returns a ranked list of movie recommendations. The frontend will display the result in the UI after receiving the response.</p> <p class="image-container"><img alt="d21427db9587560f.png" style="width: 254.50px" src="/static/tfrecommenders-flutter/img/d21427db9587560f.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/d21427db9587560f_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <img alt="73e8272a5ce8dfbc.png" style="width: 249.06px" src="/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/73e8272a5ce8dfbc_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> <p>If you click <strong>Recommend</strong> now, nothing happens because the app can't communicate with the backend yet.</p> </google-codelab-step> <google-codelab-step label="Step 1: Create the retrieval and ranking models for the recommendation engine" duration="15" step="5"> <h2 class="step-title" id="5" data-text="Step 1: Create the retrieval and ranking models for the recommendation engine" tabindex="-1"> 6. Step 1: Create the retrieval and ranking models for the recommendation engine </h2> <p>Real-world recommendation engines are often composed of multiple stages:</p> <ol type="1"> <li>The retrieval stage is responsible for selecting an initial set of hundreds of candidates from all possible candidates. The main objective of this model is to efficiently weed out all candidates that the user is not interested in. Because the retrieval model may be dealing with millions of candidates, it has to be computationally efficient.</li> <li>The ranking stage takes the outputs of the retrieval model and fine-tunes them to select the best possible handful of recommendations. Its task is to narrow down the set of items the user may be interested in to a shortlist of likely candidates in the order of hundreds.</li> <li>The post-ranking stage helps ensure diversity, freshness, and fairness and reorganizes the candidate items into a set of useful recommendations in the order of dozens.</li> </ol> <p class="image-container"><img alt="70dfc0d7e989164f.png" style="width: 618.50px" src="/static/tfrecommenders-flutter/img/70dfc0d7e989164f.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/70dfc0d7e989164f_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> <p>For this codelab, you train a retrieval model and a ranking model using the popular MovieLens dataset. You can open the training code below via Colab and follow the instructions:</p> <ul> <li><a href="https://colab.sandbox.google.com/github/flutter/codelabs/blob/master/tfrs-flutter/step1/backend/retrieval/retrieval.ipynb" target="_blank">MovieLens retrieval model</a></li> <li><a href="https://colab.sandbox.google.com/github/flutter/codelabs/blob/master/tfrs-flutter/step1/backend/ranking/ranking.ipynb" target="_blank">MovieLens ranking model</a></li> </ul> <aside class="special"><p><strong>Note:</strong> The training code is adapted from the TensorFlow Recommenders <a href="https://www.tensorflow.org/recommenders/examples/basic_retrieval" target="_blank">basic retrieval</a> and <a href="https://www.tensorflow.org/recommenders/examples/basic_ranking" target="_blank">basic ranking</a> tutorials. Please refer to the tutorials for more information. In addition, the <a href="https://www.youtube.com/watch?v=jz0-satrmrA&list=PLQY2H8rRoyvy2MiyUBz5RWZr5MPFkV3qz&index=3" target="_blank">video</a> for building the basic retrieval model and the <a href="https://www.youtube.com/watch?v=ZkwJo5HRjiQ&list=PLQY2H8rRoyvy2MiyUBz5RWZr5MPFkV3qz&index=4" target="_blank">video</a> for building the basic ranking model are useful references.</p> </aside> </google-codelab-step> <google-codelab-step label="Step 2: Create the recommendation engine backend" duration="5" step="6"> <h2 class="step-title" id="6" data-text="Step 2: Create the recommendation engine backend" tabindex="-1"> 7. Step 2: Create the recommendation engine backend </h2> <aside class="special"><p><strong>Note:</strong> The instructions below only apply to a Linux machine or an Intel-based Mac. It's possible to adapt the commands to Windows. Please refer to the docker documentation for more information. For Apple Silicon-based Macs, it won't work because TensorFlow Serving is incompatible as of January 2024.</p> </aside> <p>Now that you have trained the retrieval and ranking models, you can deploy them and create a backend.</p> <aside class="special"><p><strong>Note:</strong> Pretrained SavedModel models are provided in the <code translate="no" dir="ltr">step2/backend/retrieval/exported-retrieval/</code> and <code translate="no" dir="ltr">step2/backend/ranking/exported-ranking/</code> folders for your convenience.</p> </aside> <h2 is-upgraded id="start-tensorflow-serving" data-text="Start TensorFlow Serving" tabindex="-1">Start TensorFlow Serving</h2> <p>Since you need to use both the retrieval and ranking models to generate the recommended movie list, you deploy both of them at the same time using TensorFlow Serving.</p> <ul> <li>In your terminal, go to the <code translate="no" dir="ltr">step2/backend</code> folder on your computer and start TensorFlow Serving with Docker:</li> </ul> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>docker run -t --rm -p 8501:8501 -p 8500:8500 -v "$(pwd)/:/models/" tensorflow/serving --model_config_file=/models/models.config </pre></devsite-code> <aside class="special"><p><strong>Note:</strong> The <code translate="no" dir="ltr">models.config</code> file specifies how the two models should be deployed in TensorFlow Serving. If you train and download the models from Colab by yourself, make sure to update the model paths in the <code translate="no" dir="ltr">models.config</code> file.</p> </aside> <p>Docker automatically downloads the TensorFlow Serving image first, which takes a minute. Afterward, TensorFlow Serving should start. The log should look like this code snippet:</p> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>2022-04-24 09:32:06.461702: I tensorflow_serving/model_servers/server_core.cc:465] Adding/updating models. 2022-04-24 09:32:06.461843: I tensorflow_serving/model_servers/server_core.cc:591] (Re-)adding model: retrieval 2022-04-24 09:32:06.461907: I tensorflow_serving/model_servers/server_core.cc:591] (Re-)adding model: ranking 2022-04-24 09:32:06.576920: I tensorflow_serving/core/basic_manager.cc:740] Successfully reserved resources to load servable {name: retrieval version: 123} 2022-04-24 09:32:06.576993: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: retrieval version: 123} 2022-04-24 09:32:06.577011: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: retrieval version: 123} 2022-04-24 09:32:06.577848: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:38] Reading SavedModel from: /models/retrieval/exported-retrieval/123 2022-04-24 09:32:06.583809: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:90] Reading meta graph with tags { serve } 2022-04-24 09:32:06.583879: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:132] Reading SavedModel debug info (if present) from: /models/retrieval/exported-retrieval/123 2022-04-24 09:32:06.584970: I external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags. 2022-04-24 09:32:06.629900: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-04-24 09:32:06.634662: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2800000000 Hz 2022-04-24 09:32:06.672534: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/retrieval/exported-retrieval/123 2022-04-24 09:32:06.673629: I tensorflow_serving/core/basic_manager.cc:740] Successfully reserved resources to load servable {name: ranking version: 123} 2022-04-24 09:32:06.673765: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: ranking version: 123} 2022-04-24 09:32:06.673786: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: ranking version: 123} 2022-04-24 09:32:06.674731: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:38] Reading SavedModel from: /models/ranking/exported-ranking/123 2022-04-24 09:32:06.683557: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:90] Reading meta graph with tags { serve } 2022-04-24 09:32:06.683601: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:132] Reading SavedModel debug info (if present) from: /models/ranking/exported-ranking/123 2022-04-24 09:32:06.688665: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 110815 microseconds. 2022-04-24 09:32:06.690019: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/retrieval/exported-retrieval/123/assets.extra/tf_serving_warmup_requests 2022-04-24 09:32:06.693025: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: retrieval version: 123} 2022-04-24 09:32:06.702594: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-04-24 09:32:06.745361: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/ranking/exported-ranking/123 2022-04-24 09:32:06.772363: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 97633 microseconds. 2022-04-24 09:32:06.774853: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/ranking/exported-ranking/123/assets.extra/tf_serving_warmup_requests 2022-04-24 09:32:06.777706: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: ranking version: 123} 2022-04-24 09:32:06.778969: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models 2022-04-24 09:32:06.779030: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled 2022-04-24 09:32:06.784217: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ... [warn] getaddrinfo: address family for nodename not supported 2022-04-24 09:32:06.785748: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ... [evhttp_server.cc : 245] NET_LOG: Entering the event loop ... </pre></devsite-code> <aside class="special"><p><strong>Note:</strong> After you start the TensorFlow Serving docker image, you can visit <a href="http://localhost:8501/v1/models/%3CMODEL_NAME%3E/metadata" target="_blank">http://localhost:8501/v1/models/<MODEL_NAME>/metadata</a> to inspect the details of the input and output tensors. In this codelab, replace <MODEL_NAME> with <code translate="no" dir="ltr">retrieval</code> or <code translate="no" dir="ltr">ranking</code>.</p> </aside> <h2 is-upgraded id="create-a-new-endpoint" data-text="Create a new endpoint" tabindex="-1">Create a new endpoint</h2> <p>Since TensorFlow Serving does not support ‘chaining' multiple sequential models, you need to create a new service that connects the retrieval and ranking models.</p> <ul> <li>Add this code to the <code translate="no" dir="ltr">get_recommendations()</code> function in the <code translate="no" dir="ltr">step2/backend/recommendations.py</code> file:</li> </ul> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>user_id = request.get_json()["user_id"] retrieval_request = json.dumps({"instances": [user_id]}) retrieval_response = requests.post(RETRIEVAL_URL, data=retrieval_request) movie_candidates = retrieval_response.json()["predictions"][0]["output_2"] ranking_queries = [ {"user_id": u, "movie_title": m} for (u, m) in zip([user_id] * NUM_OF_CANDIDATES, movie_candidates) ] ranking_request = json.dumps({"instances": ranking_queries}) ranking_response = requests.post(RANKING_URL, data=ranking_request) movies_scores = list(np.squeeze(ranking_response.json()["predictions"])) ranked_movies = [ m[1] for m in sorted(list(zip(movies_scores, movie_candidates)), reverse=True) ] return make_response(jsonify({"movies": ranked_movies}), 200) </pre></devsite-code> <h2 is-upgraded id="start-the-flask-service" data-text="Start the Flask service" tabindex="-1">Start the Flask service</h2> <p>Now you can start the Flask service.</p> <ul> <li>In your terminal, go to the <code translate="no" dir="ltr">step2/backend/</code> folder and run the following:</li> </ul> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>FLASK_APP=recommender.py FLASK_ENV=development flask run </pre></devsite-code> <p>Flask will stand up a new endpoint at <code translate="no" dir="ltr">http://localhost:5000/recommend</code>. You should see the log as below:</p> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded> * Serving Flask app 'recommender.py' (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 705-382-264 127.0.0.1 - - [25/Apr/2022 19:44:47] "POST /recommend HTTP/1.1" 200 - </pre></devsite-code> <p>You could send a sample request to the endpoint to make sure it is working as expected:</p> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>curl -X POST -H "Content-Type: application/json" -d '{"user_id":"42"}' http://localhost:5000/recommend </pre></devsite-code> <p>The endpoint will return a list of recommended movies for user <code translate="no" dir="ltr">42</code>:</p> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>{ "movies": [ "While You Were Sleeping (1995)", "Preacher's Wife, The (1996)", "Michael (1996)", "Lion King, The (1994)", "Father of the Bride Part II (1995)", "Sleepless in Seattle (1993)", "101 Dalmatians (1996)", "Bridges of Madison County, The (1995)", "Rudy (1993)", "Jack (1996)" ] } </pre></devsite-code> <p>That's it! You have successfully built a backend to recommend movies based on a user ID.</p> <aside class="special"><p><strong>Note:</strong> In this codelab, you deploy both the retrieval and ranking models into a single TensorFlow Serving instance. In practice, it's common to deploy them into separate clusters to better manage the production workload.</p> </aside> </google-codelab-step> <google-codelab-step label="Step 3: Create the Flutter app for Android and iOS" duration="5" step="7"> <h2 class="step-title" id="7" data-text="Step 3: Create the Flutter app for Android and iOS" tabindex="-1"> 8. Step 3: Create the Flutter app for Android and iOS </h2> <p>The backend is ready. You can start sending requests to it to query movie recommendations from the Flutter app.</p> <p>The frontend app is fairly simple. It only has a TextField that takes in the user ID and sends the request (in the <code translate="no" dir="ltr">recommend()</code> function) to the backend you just built. After receiving the response, the app UI displays the recommended movies in a ListView.</p> <ul> <li>Add this code to the <code translate="no" dir="ltr">recommend()</code> function in the <code translate="no" dir="ltr">step3/frontend/lib/main.dart</code> file:</li> </ul> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>final response = await http.post( Uri.parse('http://' + _server + ':5000/recommend'), headers: <String, String>{ 'Content-Type': 'application/json', }, body: jsonEncode(<String, String>{ 'user_id': _userIDController.text, }), ); </pre></devsite-code> <p>Once the app receives the response from the backend, you update the UI to display the list of recommended movies for the specified user.</p> <ul> <li>Add this code right below the code above:</li> </ul> <div></div><devsite-code><pre translate="no" dir="ltr" is-upgraded>if (response.statusCode == 200) { return List<String>.from(jsonDecode(response.body)['movies']); } else { throw Exception('Error response'); } </pre></devsite-code> <h2 is-upgraded id="run-it" data-text="Run it" tabindex="-1">Run it</h2> <ol type="1"> <li>Click <img alt="a19a0c68bc4046e6.png" style="width: 25.00px" src="/static/tfrecommenders-flutter/img/a19a0c68bc4046e6.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <strong>Start debugging</strong> and then wait for the app to load.</li> <li>Enter a user ID (i.e., 42) and then select <strong>Recommend</strong>.</li> </ol> <p class="image-container"><img alt="badb59d8b96959ae.png" style="width: 262.44px" src="/static/tfrecommenders-flutter/img/badb59d8b96959ae.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/badb59d8b96959ae_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <img alt="a0d2d4020aebfb0a.png" style="width: 256.85px" src="/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a0d2d4020aebfb0a_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> </google-codelab-step> <google-codelab-step label="Step 4: Run the Flutter app on the desktop platforms" duration="10" step="8"> <h2 class="step-title" id="8" data-text="Step 4: Run the Flutter app on the desktop platforms" tabindex="-1"> 9. Step 4: Run the Flutter app on the desktop platforms </h2> <p>In addition to Android and iOS, Flutter also supports desktop platforms including Linux, Mac and Windows.</p> <h2 is-upgraded id="linux" data-text="Linux" tabindex="-1">Linux</h2> <ol type="1"> <li>Make sure the target device is set to <img alt="86cba523de82b4f9.png" style="width: 99.00px" src="/static/tfrecommenders-flutter/img/86cba523de82b4f9.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/86cba523de82b4f9_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> in the status bar of VSCode.</li> <li>Click <img alt="a19a0c68bc4046e6.png" style="width: 25.00px" src="/static/tfrecommenders-flutter/img/a19a0c68bc4046e6.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <strong>Start debugging</strong> and then wait for the app to load.</li> <li>Enter a user ID (i.e., 42) and then select <strong>Recommend</strong>.</li> </ol> <p class="image-container"><img alt="2665514231033f1.png" style="width: 624.00px" src="/static/tfrecommenders-flutter/img/2665514231033f1.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/2665514231033f1_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> <h2 is-upgraded id="mac" data-text="Mac" tabindex="-1">Mac</h2> <ol type="1"> <li>For Mac, you need to set up appropriate entitlements since the app will send HTTP requests to the backend. Please refer to <a href="https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox" target="_blank">Entitlements and the App Sandbox</a> for more details.</li> </ol> <p>Add this code to <code translate="no" dir="ltr">step4/frontend/macOS/Runner/DebugProfile.entitlements</code> and <code translate="no" dir="ltr">step4/frontend/macOS/Runner/Release.entitlements</code> respectively:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Text only"><code translate="no" dir="ltr"><key>com.apple.security.network.client</key> <true/> </code></pre></devsite-code> <ol type="1" start="2"> <li>Make sure the target device is set to <img alt="eb4b0b5563824138.png" style="width: 91.24px" src="/static/tfrecommenders-flutter/img/eb4b0b5563824138.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/eb4b0b5563824138_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> in the status bar of VSCode.</li> <li>Click <img alt="a19a0c68bc4046e6.png" style="width: 25.00px" src="/static/tfrecommenders-flutter/img/a19a0c68bc4046e6.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <strong>Start debugging</strong> and then wait for the app to load.</li> <li>Enter a user ID (i.e., 42) and then select <strong>Recommend</strong>.</li> </ol> <p class="image-container"><img alt="860d523a7ac537e0.png" style="width: 624.00px" src="/static/tfrecommenders-flutter/img/860d523a7ac537e0.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/860d523a7ac537e0_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> <h2 is-upgraded id="windows" data-text="Windows" tabindex="-1">Windows</h2> <ol type="1"> <li>Make sure the target device is set to <img alt="9587be1bb375bc0f.png" style="width: 126.08px" src="/static/tfrecommenders-flutter/img/9587be1bb375bc0f.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9587be1bb375bc0f_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> in the status bar of VSCode.</li> <li>Click <img alt="a19a0c68bc4046e6.png" style="width: 25.00px" src="/static/tfrecommenders-flutter/img/a19a0c68bc4046e6.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <strong>Start debugging</strong> and then wait for the app to load.</li> <li>Enter a user ID (i.e., 42) and then select <strong>Recommend</strong>.</li> </ol> <p class="image-container"><img alt="7d77c1e52a5927fc.png" style="width: 624.00px" src="/static/tfrecommenders-flutter/img/7d77c1e52a5927fc.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/7d77c1e52a5927fc_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> </google-codelab-step> <google-codelab-step label="Step 5: Run the Flutter app on the web platform" duration="2" step="9"> <h2 class="step-title" id="9" data-text="Step 5: Run the Flutter app on the web platform" tabindex="-1"> 10. Step 5: Run the Flutter app on the web platform </h2> <p>One more thing you can do is to add web support to the Flutter app. By default the web platform is automatically enabled for Flutter apps, so all you need to do is to launch it.</p> <ol type="1"> <li>Make sure the target device is set to <img alt="71db93efa928d15d.png" style="width: 135.24px" src="/static/tfrecommenders-flutter/img/71db93efa928d15d.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/71db93efa928d15d_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> in the status bar of VSCode.</li> <li>Click <img alt="a19a0c68bc4046e6.png" style="width: 25.00px" src="/static/tfrecommenders-flutter/img/a19a0c68bc4046e6.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/a19a0c68bc4046e6_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <strong>Start debugging</strong> and then wait for the app to load in the Chrome browser.</li> <li>Enter a user ID (i.e., 42) and then select <strong>Recommend</strong>.</li> </ol> <p class="image-container"><img alt="9376e1e432c18bef.png" style="width: 624.00px" src="/static/tfrecommenders-flutter/img/9376e1e432c18bef.png" srcset="https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_36.png 36w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_48.png 48w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_72.png 72w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_96.png 96w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_480.png 480w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_720.png 720w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_856.png 856w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_960.png 960w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_1440.png 1440w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_1920.png 1920w,https://codelabs.developers.google.com/static/tfrecommenders-flutter/img/9376e1e432c18bef_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"></p> </google-codelab-step> <google-codelab-step label="Congratulations" duration="1" step="10"> <h2 class="step-title" id="10" data-text="Congratulations" tabindex="-1"> 11. Congratulations </h2> <p>You built a fullstack app to recommend movies to your users!</p> <p>Although the app only recommends movies, you have learned the overall workflow of building a powerful recommendation engine and mastered the skill to consume the recommendations in a Flutter app. You can easily apply what you have learned to other scenarios (e.g., eCommerce, food and short videos).</p> <h2 is-upgraded id="learn-more" data-text="Learn more" tabindex="-1">Learn more</h2> <ul> <li><a href="https://www.tensorflow.org/recommenders" target="_blank">TensorFlow Reocommenders homepage</a></li> <li><a href="https://www.tensorflow.org/tfx/guide/serving" target="_blank">TensorFlow Serving homepage</a></li> <li><a href="https://flutter.dev/" target="_blank">Flutter home page</a></li> <li><a href="https://www.youtube.com/playlist?list=PLQY2H8rRoyvy2MiyUBz5RWZr5MPFkV3qz" target="_blank">Building recommendation systems with TensorFlow video series on YouTube</a></li> </ul> </google-codelab-step> </google-codelab> </div> <div class="devsite-floating-action-buttons"> </div> </article> <devsite-content-footer class="nocontent"> <p>Except as otherwise noted, the content of this page is licensed under the <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 License</a>, and code samples are licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>. For details, see the <a href="https://developers.google.com/site-policies">Google Developers Site Policies</a>. Java is a registered trademark of Oracle and/or its affiliates.</p> </devsite-content-footer> <devsite-notification > </devsite-notification> <div class="devsite-content-data"> <template class="devsite-content-data-template"> [[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],[],[],[]] </template> </div> </devsite-content> </main> <devsite-footer-promos class="devsite-footer"> </devsite-footer-promos> <devsite-footer-linkboxes class="devsite-footer"> <nav class="devsite-footer-linkboxes nocontent" aria-label="Footer links"> <ul class="devsite-footer-linkboxes-list"> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Connect</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="//googledevelopers.blogspot.com" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Blog </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//www.facebook.com/Google-Developers-967415219957038" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Facebook </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//medium.com/google-developers" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Medium </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//twitter.com/googledevs" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Twitter </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//www.youtube.com/user/GoogleDevelopers" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 5)" > YouTube </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Programs</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="//www.womentechmakers.com" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Women Techmakers </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//developers.google.com/community/gdg" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Google Developer Groups </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//developers.google.com/community/experts" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Google Developer Experts </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//developers.google.com/community/accelerators" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Accelerators </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//developers.google.com/community/gdsc" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 5)" > Google Developer Student Clubs </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Developer consoles</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="//console.developers.google.com" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Google API Console </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//console.cloud.google.com" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Google Cloud Platform Console </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//play.google.com/apps/publish" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Google Play Console </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//console.firebase.google.com" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Firebase Console </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//console.actions.google.com" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 5)" > Actions on Google Console </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//cast.google.com/publish" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 6)" > Cast SDK Developer Console </a> </li> <li class="devsite-footer-linkbox-item"> <a href="//chrome.google.com/webstore/developer/dashboard" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 7)" > Chrome Web Store Dashboard </a> </li> </ul> </li> </ul> </nav> </devsite-footer-linkboxes> <devsite-footer-utility class="devsite-footer"> <div class="devsite-footer-utility nocontent"> <nav class="devsite-footer-sites" aria-label="Other Google Developers websites"> <a href="https://developers.google.com/" class="devsite-footer-sites-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Google Developers Link"> <picture> <img class="devsite-footer-sites-logo" src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/images/lockup-google-for-developers.svg" loading="lazy" alt="Google Developers"> </picture> </a> <ul class="devsite-footer-sites-list"> <li class="devsite-footer-sites-item"> <a href="//developer.android.com" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Android Link" > Android </a> </li> <li class="devsite-footer-sites-item"> <a href="//developer.chrome.com/home" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Chrome Link" > Chrome </a> </li> <li class="devsite-footer-sites-item"> <a href="//firebase.google.com" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Firebase Link" > Firebase </a> </li> <li class="devsite-footer-sites-item"> <a href="//cloud.google.com" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Google Cloud Platform Link" > Google Cloud Platform </a> </li> <li class="devsite-footer-sites-item"> <a href="//developers.google.com/products" class="devsite-footer-sites-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer All products Link" > All products </a> </li> </ul> </nav> <nav class="devsite-footer-utility-links" aria-label="Utility links"> <ul class="devsite-footer-utility-list"> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//developers.google.com/terms/site-terms" data-category="Site-Wide Custom Events" data-label="Footer Terms link" > Terms </a> </li> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//policies.google.com/privacy" data-category="Site-Wide Custom Events" data-label="Footer Privacy link" > Privacy </a> </li> <li class="devsite-footer-utility-item glue-cookie-notification-bar-control"> <a class="devsite-footer-utility-link gc-analytics-event" href="#" data-category="Site-Wide Custom Events" data-label="Footer Manage cookies link" aria-hidden="true" > Manage cookies </a> </li> <li class="devsite-footer-utility-item devsite-footer-utility-button"> <span class="devsite-footer-utility-description">Sign up for the Google Developers newsletter</span> <a class="devsite-footer-utility-link gc-analytics-event" href="//services.google.com/fb/forms/googledevelopersnewsletter/?utm_medium=referral&utm_source=google-products&utm_team=googledevs&utm_campaign=201611-newsletter-launch" data-category="Site-Wide Custom Events" data-label="Footer Subscribe link" > Subscribe </a> </li> </ul> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es" >Español</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> </nav> </div> </devsite-footer-utility> <devsite-panel></devsite-panel> </section></section> <devsite-sitemask></devsite-sitemask> <devsite-snackbar></devsite-snackbar> <devsite-tooltip ></devsite-tooltip> <devsite-heading-link></devsite-heading-link> <devsite-analytics> <script type="application/json" analytics>[]</script> <script type="application/json" tag-management>{"at": "True", "ga4": [], "ga4p": [], "gtm": [], "parameters": {"internalUser": "False", "language": {"machineTranslated": "False", "requested": "en", "served": "en"}, "pageType": "codelab", "projectName": "Codelabs", "signedIn": "False", "tenant": "codelabs", "recommendations": {"sourcePage": "", "sourceType": 0, "sourceRank": 0, "sourceIdenticalDescriptions": 0, "sourceTitleWords": 0, "sourceDescriptionWords": 0, "experiment": ""}, "experiment": {"ids": ""}}}</script> </devsite-analytics> <devsite-badger></devsite-badger> <script nonce="l5OjWtU7kpTk5lcIiGEY7bd/7N2hvL"> (function(d,e,v,s,i,t,E){d['GoogleDevelopersObject']=i; t=e.createElement(v);t.async=1;t.src=s;E=e.getElementsByTagName(v)[0]; E.parentNode.insertBefore(t,E);})(window, document, 'script', 'https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/js/app_loader.js', '[17,"en",null,"/js/devsite_app_module.js","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs","https://codelabs-dot-devsite-v2-prod.appspot.com",1,null,["/_pwa/codelabs/manifest.json","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/images/video-placeholder.svg","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/images/favicon.png","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/codelabs/images/lockup.svg","https://fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap"],1,null,[1,6,8,12,14,17,21,25,50,52,63,70,75,76,80,87,91,92,93,97,98,100,101,102,103,104,105,107,108,109,110,112,113,116,117,118,120,122,124,125,126,127,129,130,131,132,133,134,135,136,138,140,141,147,148,149,151,152,156,157,158,159,161,163,164,168,169,170,179,180,182,183,186,191,193,196],"AIzaSyAP-jjEJBzmIyKR4F-3XITp8yM9T1gEEI8","AIzaSyB6xiKGDR5O3Ak2okS4rLkauxGUG7XP0hg","codelabs.developers.google.com","AIzaSyAQk0fBONSGUqCNznf6Krs82Ap1-NV6J4o","AIzaSyCCxcqdrZ_7QMeLCRY20bh_SXdAYqy70KY",null,null,null,["MiscFeatureFlags__developers_footer_image","MiscFeatureFlags__enable_explain_this_code","Experiments__reqs_query_experiments","Cloud__enable_cloudx_ping","Profiles__enable_awarding_url","BookNav__enable_tenant_cache_key","Search__enable_page_map","MiscFeatureFlags__developers_footer_dark_image","Profiles__enable_complete_playlist_endpoint","Profiles__enable_recognition_badges","Cloud__enable_cloud_shell","Cloud__enable_legacy_calculator_redirect","Analytics__enable_clearcut_logging","TpcFeatures__enable_mirror_tenant_redirects","EngEduTelemetry__enable_engedu_telemetry","Search__enable_dynamic_content_confidential_banner","Profiles__enable_page_saving","Profiles__enable_release_notes_notifications","MiscFeatureFlags__enable_variable_operator","TpcFeatures__enable_required_headers","Profiles__enable_public_developer_profiles","Search__enable_ai_eligibility_checks","Profiles__enable_completecodelab_endpoint","Cloud__enable_cloud_dlp_service","MiscFeatureFlags__enable_view_transitions","Profiles__enable_developer_profiles_callout","CloudShell__cloud_shell_button","CloudShell__cloud_code_overflow_menu","MiscFeatureFlags__enable_project_variables","Cloud__enable_cloud_facet_chat","DevPro__enable_cloud_innovators_plus","Cloud__enable_cloud_shell_fte_user_flow","Profiles__enable_profile_collections","MiscFeatureFlags__enable_firebase_utm","Search__enable_suggestions_from_borg","MiscFeatureFlags__emergency_css","Profiles__require_profile_eligibility_for_signin","Concierge__enable_pushui","Cloud__enable_free_trial_server_call","Cloud__enable_llm_concierge_chat","Profiles__enable_dashboard_curated_recommendations","DevPro__enable_developer_subscriptions","Cloud__enable_cloudx_experiment_ids"],null,null,"AIzaSyBLEMok-5suZ67qRPzx0qUtbnLmyT_kCVE","https://developerscontentserving-pa.clients6.google.com","AIzaSyCM4QpTRSqP5qI4Dvjt4OAScIN8sOUlO-k","https://developerscontentsearch-pa.clients6.google.com",1,4,null,"https://developerprofiles-pa.clients6.google.com",[17,"codelabs","Google Codelabs","codelabs.developers.google.com",null,"codelabs-dot-devsite-v2-prod.appspot.com",null,null,[null,1,null,null,null,null,null,null,null,null,null,[1],null,null,null,null,null,null,[1],null,null,null,null,[1]],null,[33,null,null,null,null,null,"/images/lockup.svg",null,null,null,null,1,1,null,null,null,null,null,null,null,null,1,null,null,null,null,[]],[],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[6,1,14,15,20,22,23,29,32,36],null,[[],[1,1]],[[null,null,null,null,null,null,null,null,null,null,null,null,1]],null,4],null,"pk_live_5170syrHvgGVmSx9sBrnWtA5luvk9BwnVcvIi7HizpwauFG96WedXsuXh790rtij9AmGllqPtMLfhe2RSwD6Pn38V00uBCydV4m"]') </script> <devsite-a11y-announce></devsite-a11y-announce> </body> </html>