CINXE.COM

Feature integration | Flutter

<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Feature integration | Flutter</title><link rel="icon" href="/assets/images/branding/flutter/icon/64.png" eleventy:ignore><link rel="apple-touch-icon" href="/assets/images/branding/flutter/logo/flutter-logomark-320px.png" eleventy:ignore><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#ffffff"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><meta name="google-site-verification" content="HFqxhSbf9YA_0rBglNLzDiWnrHiK_w4cqDh2YD2GEY4"><script>!function(e,t,a,n){e[n]=e[n]||[],e[n].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var g=t.getElementsByTagName(a)[0],m=t.createElement(a);m.async=!0,m.src="https://www.googletagmanager.com/gtm.js?id=GTM-ND4LWWZ",g.parentNode.insertBefore(m,g)}(window,document,"script","dataLayer")</script><script>!function(e,a,t,n,c,o,s){e.GoogleAnalyticsObject=c,e[c]=e[c]||function(){(e[c].q=e[c].q||[]).push(arguments)},e[c].l=1*new Date,o=a.createElement(t),s=a.getElementsByTagName(t)[0],o.async=1,o.src="//www.google-analytics.com/analytics.js",s.parentNode.insertBefore(o,s)}(window,document,"script",0,"ga"),ga("create","UA-67589403-1","auto"),ga("send","pageview")</script><meta name="description" content="How to integrate with other Flutter features."><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content="@flutterdev"><meta property="og:title" content="Feature integration"><meta property="og:url" content="https://docs.flutter.dev/ai-toolkit/feature-integration"><meta property="og:description" content="How to integrate with other Flutter features."><meta property="og:image" content="https://docs.flutter.dev/assets/images/flutter-logo-sharing.png" eleventy:ignore><link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Google+Sans+Text:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Google+Sans+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" rel="stylesheet"><script>window.__CALLBACKS=[]</script><link rel="stylesheet" href="/assets/css/main.css?v=4"></head><body><section id="cookie-notice"><div class="container"><p>docs.flutter.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic. <a href="https://policies.google.com/technologies/cookies" target="_blank" rel="noopener">Learn more</a>.</p><button id="cookie-consent" class="filled-button">OK, got it</button></div></section><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-ND4LWWZ" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript><div id="site-banner" role="alert"><p>Flutter 3.29 is here with a bouquet of performance and fidelity improvements for your apps! <a href="https://medium.com/flutter/whats-new-in-flutter-3-29-f90c380c2317">Learn more</a><br></p></div><header class="site-header"><a href="#document-title" id="skip-to-main" class="filled-button" tabindex="1">Skip to main content</a><nav class="navbar"><div id="site-switcher" class="dropdown"><button class="dropdown-button site-wordmark" aria-expanded="false" aria-controls="site-switcher-menu" aria-label="Switch between Flutter and Dart sites"><img src="/assets/images/branding/flutter/logo/default.svg" alt="Flutter logo" width="28"> <span>Flutter</span> <span class="subtype">Docs</span> <span class="material-symbols" aria-hidden="true">unfold_more</span></button><div class="dropdown-content" id="site-switcher-menu"><nav class="dropdown-menu" role="menu"><ul><li role="presentation"><a href="https://flutter.dev" class="site-wordmark" role="menuitem" title="Flutter homepage" aria-label="Go to the Flutter homepage"><img src="/assets/images/branding/flutter/logo/default.svg" alt="Flutter logo" width="28"> <span>Flutter</span></a></li><li role="presentation"><a href="https://docs.flutter.dev" class="site-wordmark current-site" role="menuitem" aria-current="true" title="Flutter docs homepage" aria-label="Go to the Flutter docs homepage"><img src="/assets/images/branding/flutter/logo/default.svg" alt="Flutter logo" width="28"> <span>Flutter</span> <span class="subtype">Docs</span></a></li><li role="presentation"><a href="https://api.flutter.dev" class="site-wordmark" role="menuitem" title="Flutter API reference" aria-label="Go to the Flutter API reference"><img src="/assets/images/branding/flutter/logo/default.svg" alt="Flutter logo" width="28"> <span>Flutter</span> <span class="subtype">API</span></a></li><li aria-hidden="true" class="dropdown-divider" role="separator"></li><li role="presentation"><a href="https://dart.dev" class="site-wordmark" role="menuitem" title="Dart homepage" aria-label="Go to the Dart homepage"><img src="/assets/images/branding/dart/logo.svg" alt="Dart logo" width="28" height="28"> <span>Dart</span></a></li><li role="presentation"><a href="https://dartpad.dev" class="site-wordmark" role="menuitem" title="DartPad playground" aria-label="Go to the DartPad playground"><img src="/assets/images/branding/dart/logo.svg" alt="Dart logo" width="28" height="28"> <span>DartPad</span></a></li><li role="presentation"><a href="https://pub.dev" class="site-wordmark" role="menuitem" title="pub.dev homepage" aria-label="Go to the pub.dev homepage"><img src="/assets/images/branding/dart/logo.svg" alt="Dart logo" width="28" height="28"> <span>pub.dev</span></a></li></ul></nav></div></div><div class="navbar-contents"><ul class="navbar-nav"><li class="nav-item"><a class="nav-link" href="https://flutter.dev">Homepage</a></li><li class="nav-item"><a class="nav-link" href="https://flutter.dev/community">Community</a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev">Packages</a></li><li class="nav-item"><a class="nav-link" href="https://api.flutter.dev">API reference</a></li></ul><form action="/search/" class="site-header__search"><input class="site-header__searchfield search-field" type="search" name="q" id="q" autocomplete="off" placeholder="Search" aria-label="Search"></form><a href="/search" id="fallback-search-button" class="icon-button" aria-label="Navigate to the docs search page." title="Navigate to the docs search page."><span class="material-symbols" aria-hidden="true">search</span> </a><a id="call-to-action" class="filled-button" href="/get-started/install/">Get started</a> <button id="menu-toggle" class="icon-button" type="button" aria-controls="sidenav" aria-label="Toggle navigation" title="Toggle navigation"><span class="material-symbols" aria-hidden="true">menu</span></button></div></nav></header><div id="site-below-header"><div id="site-main-row"><div id="sidenav" class="site-sidebar"><nav><ul><li class="nav-header">Get started</li><li class="nav-item"><a class="nav-link" href="/get-started/install"><div><span>Set up Flutter</span></div></a></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-3" role="button" aria-expanded="false" aria-controls="sidenav-3"><span>Learn Flutter</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-3"><li class="nav-item"><a class="nav-link" href="/get-started/learn-flutter"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/codelab"><div><span>Write your first app</span></div></a></li><li class="nav-item"><button class="nav-link collapsible" data-toggle="collapse" data-target="#sidenav-3-3" role="button" aria-expanded="true" aria-controls="sidenav-3-3"><span>Learn the fundamentals</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse show" id="sidenav-3-3"><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/dart"><div><span>Intro to Dart</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/widgets"><div><span>Widgets</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/layout"><div><span>Layout</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/state-management"><div><span>State management</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/user-input"><div><span>Handling user input</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/networking"><div><span>Networking and data</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/fundamentals/local-caching"><div><span>Local data and caching</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-3-4" role="button" aria-expanded="false" aria-controls="sidenav-3-4"><span>From another platform?</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-3-4"><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/android-devs"><div><span>Flutter for Android devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/compose-devs"><div><span>Flutter for Jetpack Compose devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/swiftui-devs"><div><span>Flutter for SwiftUI devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/uikit-devs"><div><span>Flutter for UIKit devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/react-native-devs"><div><span>Flutter for React Native devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/web-devs"><div><span>Flutter for web devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/xamarin-forms-devs"><div><span>Flutter for Xamarin.Forms devs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/declarative"><div><span>Introduction to declarative UI</span></div></a></li><li class="nav-item"><a class="nav-link" href="/get-started/flutter-for/dart-swift-concurrency"><div><span>Flutter versus Swift concurrency</span></div></a></li></ul></li><div class="sidenav-divider"></div><li class="nav-item"><a class="nav-link" href="/codelabs"><div><span>Codelabs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook"><div><span>Cookbook</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://github.com/flutter/samples" target="_blank" rel="noopener"><div><span>Demos and samples</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-4" role="button" aria-expanded="false" aria-controls="sidenav-4"><span>Stay up to date</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-4"><li class="nav-item"><a class="nav-link" href="/release/upgrade"><div><span>Upgrade</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/archive"><div><span>SDK archive</span></div></a></li><div class="sidenav-divider"></div><li class="nav-item"><a class="nav-link" href="/release/whats-new"><div><span>What's new</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/release-notes"><div><span>Release notes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/breaking-changes"><div><span>Breaking changes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/compatibility-policy"><div><span>Compatibility policy</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link active collapsible" data-toggle="collapse" data-target="#sidenav-5" role="button" aria-expanded="true" aria-controls="sidenav-5"><span>App solutions</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse show" id="sidenav-5"><li class="nav-item"><button class="nav-link active collapsible" data-toggle="collapse" data-target="#sidenav-5-1" role="button" aria-expanded="true" aria-controls="sidenav-5-1"><span>AI</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse show" id="sidenav-5-1"><li class="nav-item"><button class="nav-link active collapsible" data-toggle="collapse" data-target="#sidenav-5-1-1" role="button" aria-expanded="true" aria-controls="sidenav-5-1-1"><span>AI Toolkit guide</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse show" id="sidenav-5-1-1"><li class="nav-item"><a class="nav-link" href="/ai-toolkit"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ai-toolkit/user-experience"><div><span>User experience</span></div></a></li><li class="nav-item"><a class="nav-link active" href="/ai-toolkit/feature-integration"><div><span>Feature integration</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ai-toolkit/custom-llm-providers"><div><span>Custom LLM providers</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ai-toolkit/chat-client-sample"><div><span>Chat client sample</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="https://www.youtube.com/watch?v=1AuzJEiHjO4" target="_blank" rel="noopener"><div><span>Build with Google AI Dart SDK (video)</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-2" role="button" aria-expanded="false" aria-controls="sidenav-5-2"><span>Firebase & Firestore</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-2"><li class="nav-item"><a class="nav-link" href="/data-and-backend/firebase"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/docs/flutter" target="_blank" rel="noopener"><div><span>Discover Firebase for Flutter</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://www.youtube.com/watch?v=wUSkeTaBonA" target="_blank" rel="noopener"><div><span>Get to know Firebase for Flutter</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/codelabs/firebase-auth-in-flutter-apps" target="_blank" rel="noopener"><div><span>Add a user authentication flow to a Flutter app using FirebaseUI</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/codelabs/firebase-get-to-know-web" target="_blank" rel="noopener"><div><span>Get to know Firebase for web</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-3" role="button" aria-expanded="false" aria-controls="sidenav-5-3"><span>Games</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-3"><li class="nav-item"><a class="nav-link" href="/resources/games-toolkit"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/games/achievements-leaderboard"><div><span>Add achievements and leaderboards</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/codelabs/build-leaderboards-with-firestore#0" target="_blank" rel="noopener"><div><span>Build leaderboards with Firestore</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/plugins/google-mobile-ads"><div><span>Add advertising</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/games/firestore-multiplayer"><div><span>Add multiplayer support</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/flutter-in-app-purchases" target="_blank" rel="noopener"><div><span>Add in-app purchases</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/codelabs/firebase-auth-in-flutter-apps" target="_blank" rel="noopener"><div><span>Add user authentication</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/docs/crashlytics/get-started?platform=flutter" target="_blank" rel="noopener"><div><span>Debug using Crashlytics</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/flutter-flame-brick-breaker" target="_blank" rel="noopener"><div><span>Intro to Flame with Flutter</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-4" role="button" aria-expanded="false" aria-controls="sidenav-5-4"><span>Monetization</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-4"><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-4-1" role="button" aria-expanded="false" aria-controls="sidenav-5-4-1"><span>Advertising</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-4-1"><li class="nav-item"><a class="nav-link" href="/resources/ads-overview"><div><span>Ads overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/plugins/google-mobile-ads"><div><span>Add advertising</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/admob-ads-in-flutter" target="_blank" rel="noopener"><div><span>Add AdMob ads to your Flutter app</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/admob-inline-ads-in-flutter" target="_blank" rel="noopener"><div><span>Add an AdMob banner and native inline ads</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://developers.google.com/admob/flutter/mediation" target="_blank" rel="noopener"><div><span>Google AdMob mediation</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev/packages/interactive_media_ads" target="_blank" rel="noopener"><div><span>Interactive Media Ads SDK</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-4-2" role="button" aria-expanded="false" aria-controls="sidenav-5-4-2"><span>In-app purchases</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-4-2"><li class="nav-item"><a class="nav-link" href="/resources/in-app-purchases-overview"><div><span>In-app purchases overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/flutter-in-app-purchases" target="_blank" rel="noopener"><div><span>Add in-app purchases</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-4-3" role="button" aria-expanded="false" aria-controls="sidenav-5-4-3"><span>Payments</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-4-3"><li class="nav-item"><a class="nav-link" href="/resources/payments-overview"><div><span>Payments overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev/packages/pay" target="_blank" rel="noopener"><div><span>Google pay package</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-5" role="button" aria-expanded="false" aria-controls="sidenav-5-5"><span>Maps</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-5"><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/google-maps-in-flutter" target="_blank" rel="noopener"><div><span>Add Google maps to a Flutter app</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://developers.google.com/maps/flutter-package" target="_blank" rel="noopener"><div><span>Google Maps package</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-6" role="button" aria-expanded="false" aria-controls="sidenav-5-6"><span>News</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-5-6"><li class="nav-item"><a class="nav-link" href="/resources/news-toolkit"><div><span>Build a news app</span></div></a></li></ul></li></ul></li><li aria-hidden="true"><div class="sidenav-divider"></div></li><li class="nav-header">User interface</li><li class="nav-item"><a class="nav-link" href="/ui"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/widgets"><div><span>Widget catalog</span></div></a></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-10" role="button" aria-expanded="false" aria-controls="sidenav-10"><span>Layout</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-10"><li class="nav-item"><a class="nav-link" href="/ui/layout"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/layout/tutorial"><div><span>Build a layout</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-10-3" role="button" aria-expanded="false" aria-controls="sidenav-10-3"><span>Lists & grids</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-10-3"><li class="nav-item"><a class="nav-link" href="/cookbook/lists/basic-list"><div><span>Create and use lists</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/horizontal-list"><div><span>Create a horizontal list</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/grid-lists"><div><span>Create a grid view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/mixed-list"><div><span>Create lists with different types of items</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/spaced-items"><div><span>Create lists with spaced items</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/long-lists"><div><span>Work with long lists</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-10-4" role="button" aria-expanded="false" aria-controls="sidenav-10-4"><span>Scrolling</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-10-4"><li class="nav-item"><a class="nav-link" href="/ui/layout/scrolling"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/layout/scrolling/slivers"><div><span>Use slivers to achieve fancy scrolling</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/lists/floating-app-bar"><div><span>Place a floating app bar above a list</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/effects/parallax-scrolling"><div><span>Create a scrolling parallax effect</span></div></a></li></ul></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-11" role="button" aria-expanded="false" aria-controls="sidenav-11"><span>Adaptive & responsive design</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-11"><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/general"><div><span>General approach</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/safearea-mediaquery"><div><span>SafeArea & MediaQuery</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/large-screens"><div><span>Large screens & foldables</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/input"><div><span>User input & accessibility</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/capabilities"><div><span>Capabilities & policies</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/platform-adaptations"><div><span>Automatic platform adaptations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/best-practices"><div><span>Best practices</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/adaptive-responsive/more-info"><div><span>Additional resources</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-12" role="button" aria-expanded="false" aria-controls="sidenav-12"><span>Design & theming</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-12"><li class="nav-item"><a class="nav-link" href="/cookbook/design/themes"><div><span>Share styles with themes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/design/material"><div><span>Material design</span></div></a></li><li class="nav-item"><a class="nav-link" href="/release/breaking-changes/material-3-migration"><div><span>Migrate to Material 3</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-12-4" role="button" aria-expanded="false" aria-controls="sidenav-12-4"><span>Text</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-12-4"><li class="nav-item"><a class="nav-link" href="/ui/design/text/typography"><div><span>Fonts & typography</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/fonts"><div><span>Use a custom font</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/package-fonts"><div><span>Export fonts from a package</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev/packages/google_fonts" target="_blank" rel="noopener"><div><span>Google Fonts package</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-12-5" role="button" aria-expanded="false" aria-controls="sidenav-12-5"><span>Custom graphics</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-12-5"><li class="nav-item"><a class="nav-link" href="/ui/design/graphics/fragment-shaders"><div><span>Use custom fragment shaders</span></div></a></li></ul></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-13" role="button" aria-expanded="false" aria-controls="sidenav-13"><span>Interactivity</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-13"><li class="nav-item"><a class="nav-link" href="/ui/interactivity"><div><span>Add interactivity to your app</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-13-2" role="button" aria-expanded="false" aria-controls="sidenav-13-2"><span>Gestures</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-13-2"><li class="nav-item"><a class="nav-link" href="/ui/interactivity/gestures"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/gestures/handling-taps"><div><span>Handle taps</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/interactivity/gestures/drag-outside"><div><span>Drag an object outside an app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/effects/drag-a-widget"><div><span>Drag a UI element within an app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/gestures/ripples"><div><span>Add Material touch ripples</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/gestures/dismissible"><div><span>Implement swipe to dismiss</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-13-3" role="button" aria-expanded="false" aria-controls="sidenav-13-3"><span>Input & forms</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-13-3"><li class="nav-item"><a class="nav-link" href="/cookbook/forms/text-input"><div><span>Create and style a text field</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/forms/retrieve-input"><div><span>Retrieve the value of a text field</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/forms/text-field-changes"><div><span>Handle changes to a text field</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/forms/focus"><div><span>Manage focus in text fields</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/forms/validation"><div><span>Build a form with validation</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/snackbars"><div><span>Display a snackbar</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/interactivity/actions-and-shortcuts"><div><span>Implement actions & shortcuts</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/interactivity/focus"><div><span>Manage keyboard focus</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-14" role="button" aria-expanded="false" aria-controls="sidenav-14"><span>Assets & media</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-14"><li class="nav-item"><a class="nav-link" href="/ui/assets/assets-and-images"><div><span>Add assets and images</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/images/network-image"><div><span>Display images from the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/images/fading-in-images"><div><span>Fade in images with a placeholder</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/plugins/play-video"><div><span>Play and pause a video</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/assets/asset-transformation"><div><span>Transform assets at build time</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-15" role="button" aria-expanded="false" aria-controls="sidenav-15"><span>Navigation & routing</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-15"><li class="nav-item"><a class="nav-link" href="/ui/navigation"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/tabs"><div><span>Add tabs to your app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/navigation-basics"><div><span>Navigate to a new screen and back</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/passing-data"><div><span>Send data to a new screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/returning-data"><div><span>Return data from a screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/design/drawer"><div><span>Add a drawer to a screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/navigation/deep-linking"><div><span>Set up deep linking</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/set-up-app-links"><div><span>Set up app links for Android</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/navigation/set-up-universal-links"><div><span>Set up universal links for iOS</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/navigation/url-strategies"><div><span>Configure web URL strategies</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-16" role="button" aria-expanded="false" aria-controls="sidenav-16"><span>Animations & transitions</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-16"><li class="nav-item"><a class="nav-link" href="/ui/animations"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/tutorial"><div><span>Tutorial</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/implicit-animations"><div><span>Implicit animations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/animation/animated-container"><div><span>Animate the properties of a container</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/animation/opacity-animation"><div><span>Fade a widget in and out</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/hero-animations"><div><span>Hero animations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/animation/page-route-animation"><div><span>Animate a page route transition</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/animation/physics-simulation"><div><span>Animate using a physics simulation</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/staggered-animations"><div><span>Staggered animations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/effects/staggered-menu-animation"><div><span>Create a staggered menu animation</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/animations/overview"><div><span>API overview</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-17" role="button" aria-expanded="false" aria-controls="sidenav-17"><span>Accessibility & internationalization</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-17"><li class="nav-item"><a class="nav-link" href="/ui/accessibility-and-internationalization/accessibility"><div><span>Accessibility</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/accessibility-and-internationalization/internationalization"><div><span>Internationalization</span></div></a></li></ul></li><li aria-hidden="true"><div class="sidenav-divider"></div></li><li class="nav-header">Beyond UI</li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-20" role="button" aria-expanded="false" aria-controls="sidenav-20"><span>Data & backend</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20"><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-1" role="button" aria-expanded="false" aria-controls="sidenav-20-1"><span>State management</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-1"><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/intro"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/declarative"><div><span>Think declaratively</span></div></a></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/ephemeral-vs-app"><div><span>Ephemeral vs app state</span></div></a></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/simple"><div><span>Simple app state management</span></div></a></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/state-mgmt/options"><div><span>Options</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-2" role="button" aria-expanded="false" aria-controls="sidenav-20-2"><span>Networking & http</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-2"><li class="nav-item"><a class="nav-link" href="/data-and-backend/networking"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/fetch-data"><div><span>Fetch data from the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/authenticated-requests"><div><span>Make authenticated requests</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/send-data"><div><span>Send data to the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/update-data"><div><span>Update data over the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/delete-data"><div><span>Delete data on the internet</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/web-sockets"><div><span>Communicate with WebSockets</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-3" role="button" aria-expanded="false" aria-controls="sidenav-20-3"><span>Serialization</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-3"><li class="nav-item"><a class="nav-link" href="/data-and-backend/serialization/json"><div><span>JSON serialization</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/networking/background-parsing"><div><span>Parse JSON in the background</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-4" role="button" aria-expanded="false" aria-controls="sidenav-20-4"><span>Persistence</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-4"><li class="nav-item"><a class="nav-link" href="/cookbook/persistence/key-value"><div><span>Store key-value data on disk</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/persistence/reading-writing-files"><div><span>Read and write files</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/persistence/sqlite"><div><span>Persist data with SQLite</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-20-5" role="button" aria-expanded="false" aria-controls="sidenav-20-5"><span>Firebase</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-20-5"><li class="nav-item"><a class="nav-link" href="/data-and-backend/firebase"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://firebase.google.com/docs/flutter/setup" target="_blank" rel="noopener"><div><span>Add Firebase to your Flutter app</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/data-and-backend/google-apis"><div><span>Google APIs</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-21" role="button" aria-expanded="false" aria-controls="sidenav-21"><span>App architecture</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-21"><li class="nav-item"><a class="nav-link" href="/app-architecture"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/concepts"><div><span>Architecture concepts</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/guide"><div><span>Guide to app architecture</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-21-4" role="button" aria-expanded="false" aria-controls="sidenav-21-4"><span>Architecture case study</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-21-4"><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study/ui-layer"><div><span>UI layer</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study/data-layer"><div><span>Data layer</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study/dependency-injection"><div><span>Dependency injection</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/case-study/testing"><div><span>Testing each layer</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/app-architecture/recommendations"><div><span>Recommendations</span></div></a></li><li class="nav-item"><a class="nav-link" href="/app-architecture/design-patterns"><div><span>Design patterns</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-22" role="button" aria-expanded="false" aria-controls="sidenav-22"><span>Platform integration</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22"><li class="nav-item"><a class="nav-link" href="/reference/supported-platforms"><div><span>Supported platforms</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/desktop"><div><span>Build desktop apps with Flutter</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/platform-channels"><div><span>Write platform-specific code</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-4" role="button" aria-expanded="false" aria-controls="sidenav-22-4"><span>Android</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-4"><li class="nav-item"><a class="nav-link" href="/platform-integration/android/install-android"><div><span>Add Android as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/splash-screen"><div><span>Add a splash screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/predictive-back"><div><span>Add predictive back</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/c-interop"><div><span>Bind to native code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/platform-views"><div><span>Host a native Android view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/call-jetpack-apis"><div><span>Calling JetPack APIs</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/compose-activity"><div><span>Launch a Jetpack Compose activity</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/restore-state-android"><div><span>Restore state on Android</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/android/chromeos"><div><span>Target ChromeOS with Android</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-5" role="button" aria-expanded="false" aria-controls="sidenav-22-5"><span>iOS</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-5"><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/install-ios"><div><span>Add iOS as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/ios-latest"><div><span>Flutter on latest iOS</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/apple-frameworks"><div><span>Leverage Apple's system libraries</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/launch-screen"><div><span>Add a launch screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/ios-app-clip"><div><span>Add iOS App Clip support</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/app-extensions"><div><span>Add iOS app extensions</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/c-interop"><div><span>Bind to native code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/platform-views"><div><span>Host a native iOS view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/ios-debugging"><div><span>Enable debugging on iOS</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/ios/restore-state-ios"><div><span>Restore state on iOS</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-6" role="button" aria-expanded="false" aria-controls="sidenav-22-6"><span>Linux</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-6"><li class="nav-item"><a class="nav-link" href="/platform-integration/linux/install-linux"><div><span>Add Linux as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/linux/building"><div><span>Build a Linux app</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-7" role="button" aria-expanded="false" aria-controls="sidenav-22-7"><span>macOS</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-7"><li class="nav-item"><a class="nav-link" href="/platform-integration/macos/install-macos"><div><span>Add macOS as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/macos/building"><div><span>Build a macOS app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/macos/c-interop"><div><span>Bind to native code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/macos/platform-views"><div><span>Host a native macOS view</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-8" role="button" aria-expanded="false" aria-controls="sidenav-22-8"><span>Web</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-8"><li class="nav-item"><a class="nav-link" href="/platform-integration/web"><div><span>Web support in Flutter</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/install-web"><div><span>Add web as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/building"><div><span>Build a web app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/wasm"><div><span>Compile to WebAssembly</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/initialization"><div><span>Customize app initialization</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/embedding-flutter-web"><div><span>Add Flutter to any web app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/web-content-in-flutter"><div><span>Web content in Flutter</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/renderers"><div><span>Web renderers</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/web-images"><div><span>Display images on the web</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/faq"><div><span>Web FAQ</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-22-9" role="button" aria-expanded="false" aria-controls="sidenav-22-9"><span>Windows</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-22-9"><li class="nav-item"><a class="nav-link" href="/platform-integration/windows/install-windows"><div><span>Add Windows as build target</span></div></a></li><li class="nav-item"><a class="nav-link" href="/platform-integration/windows/building"><div><span>Build a Windows app</span></div></a></li></ul></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-23" role="button" aria-expanded="false" aria-controls="sidenav-23"><span>Packages & plugins</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-23"><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/using-packages"><div><span>Use packages & plugins</span></div></a></li><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/developing-packages"><div><span>Develop packages & plugins</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-23-3" role="button" aria-expanded="false" aria-controls="sidenav-23-3"><span>Swift Package Manager</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-23-3"><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/swift-package-manager/for-app-developers"><div><span>For app developers</span></div></a></li><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/swift-package-manager/for-plugin-authors"><div><span>For plugin authors</span></div></a></li></ul></li><div class="sidenav-divider"></div><li class="nav-item"><a class="nav-link" href="/packages-and-plugins/favorites"><div><span>Flutter Favorites</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev/flutter" target="_blank" rel="noopener"><div><span>Package repository</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-24" role="button" aria-expanded="false" aria-controls="sidenav-24"><span>Testing & debugging</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-24"><li class="nav-header">Testing</li><li class="nav-item"><a class="nav-link" href="/testing/overview"><div><span>Overview</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-24-3" role="button" aria-expanded="false" aria-controls="sidenav-24-3"><span>Unit testing</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-24-3"><li class="nav-item"><a class="nav-link" href="/cookbook/testing/unit/introduction"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/unit/mocking"><div><span>Mock dependencies</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-24-4" role="button" aria-expanded="false" aria-controls="sidenav-24-4"><span>Widget testing</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-24-4"><li class="nav-item"><a class="nav-link" href="/cookbook/testing/widget/introduction"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/widget/finders"><div><span>Find widgets</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/widget/scrolling"><div><span>Simulate scrolling</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/widget/tap-drag"><div><span>Simulate user interaction</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-24-5" role="button" aria-expanded="false" aria-controls="sidenav-24-5"><span>Integration testing</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-24-5"><li class="nav-item"><a class="nav-link" href="/cookbook/testing/integration/introduction"><div><span>Introduction</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/integration-tests"><div><span>Write and run an integration test</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/testing/integration/profiling"><div><span>Profile an integration test</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/testing/testing-plugins"><div><span>Test a plugin</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/plugins-in-tests"><div><span>Handle plugin code in tests</span></div></a></li><li class="nav-header">Debugging</li><li class="nav-item"><a class="nav-link" href="/testing/debugging"><div><span>Debugging tools</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/code-debugging"><div><span>Debug your app programmatically</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/native-debugging"><div><span>Use a native language debugger</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/common-errors"><div><span>Common Flutter errors</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/errors"><div><span>Handle errors</span></div></a></li><li class="nav-item"><a class="nav-link" href="/cookbook/maintenance/error-reporting"><div><span>Report errors to a service</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-25" role="button" aria-expanded="false" aria-controls="sidenav-25"><span>Performance & optimization</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-25"><li class="nav-item"><a class="nav-link" href="/perf"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/impeller"><div><span>Impeller</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/best-practices"><div><span>Performance best practices</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/app-size"><div><span>App size</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/deferred-components"><div><span>Deferred components</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/rendering-performance"><div><span>Rendering performance</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/ui-performance"><div><span>Performance profiling</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/web-performance"><div><span>Performance profiling for web</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/metrics"><div><span>Performance metrics</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/isolates"><div><span>Concurrency and isolates</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/faq"><div><span>Performance FAQ</span></div></a></li><li class="nav-item"><a class="nav-link" href="/perf/appendix"><div><span>Appendix</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-26" role="button" aria-expanded="false" aria-controls="sidenav-26"><span>Deployment</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-26"><li class="nav-item"><a class="nav-link" href="/deployment/obfuscate"><div><span>Obfuscate Dart code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/flavors"><div><span>Create app flavors for Android</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/flavors-ios"><div><span>Create app flavors for iOS and macOS</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/android"><div><span>Build and release an Android app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/ios"><div><span>Build and release an iOS app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/macos"><div><span>Build and release a macOS app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/linux"><div><span>Build and release a Linux app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/windows"><div><span>Build and release a Windows app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/web"><div><span>Build and release a web app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/deployment/cd"><div><span>Set up continuous deployment</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-27" role="button" aria-expanded="false" aria-controls="sidenav-27"><span>Add to an existing app</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-27"><li class="nav-item"><a class="nav-link" href="/add-to-app"><div><span>Introduction</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-27-2" role="button" aria-expanded="false" aria-controls="sidenav-27-2"><span>Add to an Android app</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-27-2"><li class="nav-item"><a class="nav-link" href="/add-to-app/android/project-setup"><div><span>Set up Android project</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/android/add-flutter-screen"><div><span>Add a single Flutter screen</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/android/add-flutter-fragment"><div><span>Add a Flutter Fragment</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/android/add-flutter-view"><div><span>Add a Flutter View</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/android/plugin-setup"><div><span>Use a Flutter plugin</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-27-3" role="button" aria-expanded="false" aria-controls="sidenav-27-3"><span>Add to an iOS app</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-27-3"><li class="nav-item"><a class="nav-link" href="/add-to-app/ios/project-setup"><div><span>Set up iOS project</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/ios/add-flutter-screen"><div><span>Add a single Flutter screen</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/platform-integration/web/embedding-flutter-web"><div><span>Add to a web app</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/debugging"><div><span>Debug embedded Flutter module</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/multiple-flutters"><div><span>Add multiple Flutter instances</span></div></a></li><li class="nav-item"><a class="nav-link" href="/add-to-app/performance"><div><span>Loading sequence and performance</span></div></a></li></ul></li><li aria-hidden="true"><div class="sidenav-divider"></div></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-29" role="button" aria-expanded="false" aria-controls="sidenav-29"><span>Tools & editors</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-29"><li class="nav-item"><a class="nav-link" href="/tools/android-studio"><div><span>Android Studio & IntelliJ</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/vs-code"><div><span>Visual Studio Code</span></div></a></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-29-3" role="button" aria-expanded="false" aria-controls="sidenav-29-3"><span>DevTools</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-29-3"><li class="nav-item"><a class="nav-link" href="/tools/devtools"><div><span>Overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/android-studio"><div><span>Run from Android Studio & IntelliJ</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/vscode"><div><span>Run from VS Code</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/cli"><div><span>Run from command line</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/inspector"><div><span>Flutter inspector</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/legacy-inspector"><div><span>Legacy Flutter inspector</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/performance"><div><span>Performance view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/cpu-profiler"><div><span>CPU Profiler view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/memory"><div><span>Memory view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/console"><div><span>Debug console view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/network"><div><span>Network view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/debugger"><div><span>Debugger</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/logging"><div><span>Logging view</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/app-size"><div><span>App size tool</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/extensions"><div><span>DevTools extensions</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/deep-links"><div><span>Validate deep links</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/devtools/release-notes"><div><span>Release notes</span></div></a></li></ul></li><li class="nav-item"><a class="nav-link" href="/tools/sdk"><div><span>SDK overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/pubspec"><div><span>Flutter's pubspec options</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/flutter-fix"><div><span>Automated fixes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/formatting"><div><span>Code formatting</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-30" role="button" aria-expanded="false" aria-controls="sidenav-30"><span>Flutter concepts</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-30"><li class="nav-item"><a class="nav-link" href="/resources/architectural-overview"><div><span>Architectural overview</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/inside-flutter"><div><span>Inside Flutter</span></div></a></li><li class="nav-item"><a class="nav-link" href="/ui/layout/constraints"><div><span>Understanding constraints</span></div></a></li><li class="nav-item"><a class="nav-link" href="/testing/build-modes"><div><span>Flutter's build modes</span></div></a></li><li class="nav-item"><a class="nav-link" href="/tools/hot-reload"><div><span>Hot reload</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsed collapsible" data-toggle="collapse" data-target="#sidenav-31" role="button" aria-expanded="false" aria-controls="sidenav-31"><span>Resources</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-31"><li class="nav-item"><a class="nav-link" href="/resources/faq"><div><span>FAQ</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/books"><div><span>Books</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/videos"><div><span>Videos</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/courses"><div><span>Courses</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/bootstrap-into-dart"><div><span>Learn Dart</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/support"><div><span>Get support</span></div></a></li><div class="sidenav-divider"></div><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-31-8" role="button" aria-expanded="false" aria-controls="sidenav-31-8"><span>Contribute</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-31-8"><li class="nav-item"><a class="nav-link" href="/resources/bug-reports"><div><span>Create useful bug reports</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://github.com/flutter/flutter/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener"><div><span>Contribute to Flutter</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li><li class="nav-item"><a class="nav-link" href="/resources/design-docs"><div><span>Discover proposed features</span></div></a></li></ul></li><li class="nav-item"><button class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-31-9" role="button" aria-expanded="false" aria-controls="sidenav-31-9"><span>Reference</span> <span class="material-symbols expander" aria-hidden="true">expand_more</span></button><ul class="nav collapse" id="sidenav-31-9"><li class="nav-item"><a class="nav-link" href="/dash"><div><span>Who is Dash?</span></div></a></li><li class="nav-item"><a class="nav-link" href="/reference/widgets"><div><span>Widget index</span></div></a></li><li class="nav-item"><a class="nav-link" href="/reference/flutter-cli"><div><span>flutter CLI</span></div></a></li><li class="nav-item"><a class="nav-link" href="https://api.flutter.dev" target="_blank" rel="noopener"><div><span>API docs</span><span class="material-symbols" aria-hidden="true">open_in_new</span></div></a></li></ul></li></ul></li></ul></nav></div><main class="site-content"><div id="site-toc--side" class="site-toc"><header class="site-toc__title">Contents</header><ul class="section-nav"><li class="toc-entry nav-item"><a class="nav-link" href="#welcome-messages">Welcome messages</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#suggested-prompts">Suggested prompts</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#llm-instructions">LLM instructions</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#managing-history">Managing history</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#chat-serializationdeserialization">Chat serialization/deserialization</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#custom-response-widgets">Custom response widgets</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#custom-styling">Custom styling</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#chat-without-ui">Chat without UI</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#rerouting-prompts">Rerouting prompts</a></li></ul></div><article><header class="site-content__title"><h1 id="document-title">Feature integration</h1><nav class="breadcrumbs" aria-label="breadcrumb"><ol vocab="https://schema.org/" typeof="BreadcrumbList"><li class="breadcrumb-item" property="itemListElement" typeof="ListItem"><a href="/ai-toolkit" property="item" typeof="WebPage"><span property="name">AI Toolkit</span></a><meta property="position" content="0"><span class="material-symbols child-icon" aria-hidden="true">chevron_right</span></li><li class="breadcrumb-item active" property="itemListElement" typeof="ListItem" aria-current="page"><a href="/ai-toolkit/feature-integration" property="item" typeof="WebPage"><span property="name">Feature integration</span></a><meta property="position" content="1"></li></ol></nav></header><div id="site-toc--inline" class="site-toc"><header class="site-toc__title">Contents</header><ul class="section-nav"><li class="toc-entry"><a href="#welcome-messages">Welcome messages</a></li><li class="toc-entry"><a href="#suggested-prompts">Suggested prompts</a></li><li class="toc-entry"><a href="#llm-instructions">LLM instructions</a></li><li class="toc-entry"><a href="#managing-history">Managing history</a></li><li class="toc-entry"><a href="#chat-serializationdeserialization">Chat serialization/deserialization</a></li><li class="toc-entry"><a href="#custom-response-widgets">Custom response widgets</a></li><li class="toc-entry"><a href="#custom-styling">Custom styling</a></li><li class="toc-entry"><a href="#chat-without-ui">Chat without UI</a></li><li class="toc-entry"><a href="#rerouting-prompts">Rerouting prompts</a></li></ul></div><p>In addition to the features that are provided automatically by the <a href="https://pub.dev/documentation/flutter_ai_toolkit/latest/flutter_ai_toolkit/LlmChatView-class.html"><code>LlmChatView</code></a>, a number of integration points allow your app to blend seamlessly with other features to provide additional functionality:</p><ul><li><strong>Welcome messages</strong>: Display an initial greeting to users.</li><li><strong>Suggested prompts</strong>: Offer users predefined prompts to guide interactions.</li><li><strong>System instructions</strong>: Provide the LLM with specific input to influence its responses.</li><li><strong>Managing history</strong>: Every LLM provider allows for managing chat history, which is useful for clearing it, changing it dynamically and storing it between sessions.</li><li><strong>Chat serialization/deserialization</strong>: Store and retrieve conversations between app sessions.</li><li><strong>Custom response widgets</strong>: Introduce specialized UI components to present LLM responses.</li><li><strong>Custom styling</strong>: Define unique visual styles to match the chat appearance to the overall app.</li><li><strong>Chat w/o UI</strong>: Interact directly with the LLM providers without affecting the user's current chat session.</li><li><strong>Custom LLM providers</strong>: Build your own LLM provider for integration of chat with your own model backend.</li><li><strong>Rerouting prompts</strong>: Debug, log, or reroute messages meant for the provider to track down issues or route prompts dynamically.</li></ul><div class="header-wrapper"><h2 id="welcome-messages">Welcome messages</h2><a class="heading-link" href="#welcome-messages" aria-label="Link to 'Welcome messages' section">#</a></div><p>The chat view allows you to provide a custom welcome message to set context for the user:</p><p><img src="/assets/images/docs/ai-toolkit/example-of-welcome-message.png" alt="Example welcome message"></p><p>You can initialize the <code>LlmChatView</code> with a welcome message by setting the <code>welcomeMessage</code> parameter:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> ChatPage</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> StatelessWidget</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> const</span><span style="color:#0468D7"> ChatPage</span><span style="color:#222222">({</span><span style="color:#BD2314">super</span><span style="color:#222222">.key});</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> @override</span></span> <span class="line"><span style="color:#0468D7"> Widget</span><span style="color:#6200EE"> build</span><span style="color:#222222">(</span><span style="color:#0468D7">BuildContext</span><span style="color:#222222"> context) => </span><span style="color:#0468D7">Scaffold</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> appBar: </span><span style="color:#0468D7">AppBar</span><span style="color:#222222">(title: </span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Text</span><span style="color:#222222">(</span><span style="color:#0468D7">App</span><span style="color:#222222">.title)),</span></span> <span class="line"><span style="color:#222222"> body: </span><span style="color:#0468D7">LlmChatView</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> welcomeMessage: </span><span style="color:#0C7064">'Hello and welcome to the Flutter AI Toolkit!'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> provider: </span><span style="color:#0468D7">GeminiProvider</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> model: </span><span style="color:#0468D7">GenerativeModel</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> model: </span><span style="color:#0C7064">'gemini-1.5-flash'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> apiKey: geminiApiKey,</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>To see a complete example of setting the welcome message, check out the <a href="https://github.com/flutter/ai/blob/main/example/lib/welcome/welcome.dart">welcome example</a>.</p><div class="header-wrapper"><h2 id="suggested-prompts">Suggested prompts</h2><a class="heading-link" href="#suggested-prompts" aria-label="Link to 'Suggested prompts' section">#</a></div><p>You can provide a set of suggested prompts to give the user some idea of what the chat session has been optimized for:</p><p><img src="/assets/images/docs/ai-toolkit/example-of-suggested-prompts.png" alt="Example suggested prompts"></p><p>The suggestions are only shown when there is no existing chat history. Clicking one copies the text into the user's prompt editing area. To set the list of suggestions, construct the <code>LlmChatView</code> with the <code>suggestions</code> parameter:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> ChatPage</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> StatelessWidget</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> const</span><span style="color:#0468D7"> ChatPage</span><span style="color:#222222">({</span><span style="color:#BD2314">super</span><span style="color:#222222">.key});</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> @override</span></span> <span class="line"><span style="color:#0468D7"> Widget</span><span style="color:#6200EE"> build</span><span style="color:#222222">(</span><span style="color:#0468D7">BuildContext</span><span style="color:#222222"> context) => </span><span style="color:#0468D7">Scaffold</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> appBar: </span><span style="color:#0468D7">AppBar</span><span style="color:#222222">(title: </span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Text</span><span style="color:#222222">(</span><span style="color:#0468D7">App</span><span style="color:#222222">.title)),</span></span> <span class="line"><span style="color:#222222"> body: </span><span style="color:#0468D7">LlmChatView</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> suggestions: [</span></span> <span class="line"><span style="color:#0C7064"> 'I</span><span style="color:#222222">\'</span><span style="color:#0C7064">m a Star Wars fan. What should I wear for Halloween?'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#0C7064"> 'I</span><span style="color:#222222">\'</span><span style="color:#0C7064">m allergic to peanuts. What candy should I avoid at Halloween?'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#0C7064"> 'What</span><span style="color:#222222">\'</span><span style="color:#0C7064">s the difference between a pumpkin and a squash?'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> ],</span></span> <span class="line"><span style="color:#222222"> provider: </span><span style="color:#0468D7">GeminiProvider</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> model: </span><span style="color:#0468D7">GenerativeModel</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> model: </span><span style="color:#0C7064">'gemini-1.5-flash'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> apiKey: geminiApiKey,</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>To see a complete example of setting up suggestions for the user, take a look at the <a href="https://github.com/flutter/ai/blob/main/example/lib/suggestions/suggestions.dart">suggestions example</a>.</p><div class="header-wrapper"><h2 id="llm-instructions">LLM instructions</h2><a class="heading-link" href="#llm-instructions" aria-label="Link to 'LLM instructions' section">#</a></div><p>To optimize an LLM's responses based on the needs of your app, you'll want to give it instructions. For example, the <a href="https://github.com/flutter/ai/tree/main/example/lib/recipes">recipes example app</a> uses the <code>systemInstructions</code> parameter of the <code>GenerativeModel</code> class to tailor the LLM to focus on delivering recipes based on the user's instructions:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> _HomePageState</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> State</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">HomePage</span><span style="color:#222222">> {</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#6E6E70"> // create a new provider with the given history and the current settings</span></span> <span class="line"><span style="color:#0468D7"> LlmProvider</span><span style="color:#6200EE"> _createProvider</span><span style="color:#222222">([</span><span style="color:#0468D7">List</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">ChatMessage</span><span style="color:#222222">>? history]) => </span><span style="color:#0468D7">GeminiProvider</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> history: history,</span></span> <span class="line"><span style="color:#222222"> ...,</span></span> <span class="line"><span style="color:#222222"> model: </span><span style="color:#0468D7">GenerativeModel</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> model: </span><span style="color:#0C7064">'gemini-1.5-flash'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> apiKey: geminiApiKey,</span></span> <span class="line"><span style="color:#222222"> ...,</span></span> <span class="line"><span style="color:#222222"> systemInstruction: </span><span style="color:#0468D7">Content</span><span style="color:#222222">.</span><span style="color:#6200EE">system</span><span style="color:#222222">(</span><span style="color:#0C7064">'''</span></span> <span class="line"><span style="color:#0C7064">You are a helpful assistant that generates recipes based on the ingredients and </span></span> <span class="line"><span style="color:#0C7064">instructions provided as well as my food preferences, which are as follows:</span></span> <span class="line"><span style="color:#0C7064">${</span><span style="color:#0468D7">Settings</span><span style="color:#0C7064">.</span><span style="color:#222222">foodPreferences</span><span style="color:#0C7064">.</span><span style="color:#222222">isEmpty</span><span style="color:#0C7064"> ? </span><span style="color:#0C7064">'I don</span><span style="color:#222222">\'</span><span style="color:#0C7064">t have any food preferences'</span><span style="color:#0C7064"> : </span><span style="color:#0468D7">Settings</span><span style="color:#0C7064">.</span><span style="color:#222222">foodPreferences</span><span style="color:#0C7064">}</span></span> <span class="line"></span> <span class="line"><span style="color:#0C7064">You should keep things casual and friendly. You may generate multiple recipes in a single response, but only if asked. ...</span></span> <span class="line"><span style="color:#0C7064">'''</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Setting system instructions is unique to each provider; both the <code>GeminiProvider</code> and the <code>VertexProvider</code> allow you to provide them through the <code>systemInstruction</code> parameter.</p><p>Notice that, in this case, we're bringing in user preferences as part of the creation of the LLM provider passed to the <code>LlmChatView</code> constructor. We set the instructions as part of the creation process each time the user changes their preferences. The recipes app allows the user to change their food preferences using a drawer on the scaffold:</p><p><img src="/assets/images/docs/ai-toolkit/setting-food-preferences.png" alt="Example of refining prompt"></p><p>Whenever the user changes their food preferences, the recipes app creates a new model to use the new preferences:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> _HomePageState</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> State</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">HomePage</span><span style="color:#222222">> {</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#BD2314"> void</span><span style="color:#6200EE"> _onSettingsSave</span><span style="color:#222222">() => </span><span style="color:#6200EE">setState</span><span style="color:#222222">(() {</span></span> <span class="line"><span style="color:#6E6E70"> // move the history over from the old provider to the new one</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> history = _provider.history.</span><span style="color:#6200EE">toList</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#222222"> _provider = </span><span style="color:#6200EE">_createProvider</span><span style="color:#222222">(history);</span></span> <span class="line"><span style="color:#222222"> });</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h2 id="managing-history">Managing history</h2><a class="heading-link" href="#managing-history" aria-label="Link to 'Managing history' section">#</a></div><p>The <a href="https://pub.dev/documentation/flutter_ai_toolkit/latest/flutter_ai_toolkit/LlmProvider-class.html">standard interface that defines all LLM providers</a> that can plug into the chat view includes the ability to get and set history for the provider:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">abstract</span><span style="color:#BD2314"> class</span><span style="color:#0468D7"> LlmProvider</span><span style="color:#BD2314"> implements</span><span style="color:#0468D7"> Listenable</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#0468D7"> Stream</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">String</span><span style="color:#222222">> </span><span style="color:#6200EE">generateStream</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0468D7"> String</span><span style="color:#222222"> prompt, {</span></span> <span class="line"><span style="color:#0468D7"> Iterable</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">Attachment</span><span style="color:#222222">> attachments,</span></span> <span class="line"><span style="color:#222222"> });</span></span> <span class="line"></span> <span class="line"><span style="color:#0468D7"> Stream</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">String</span><span style="color:#222222">> </span><span style="color:#6200EE">sendMessageStream</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0468D7"> String</span><span style="color:#222222"> prompt, {</span></span> <span class="line"><span style="color:#0468D7"> Iterable</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">Attachment</span><span style="color:#222222">> attachments,</span></span> <span class="line"><span style="color:#222222"> });</span></span> <span class="line"></span> <span class="line"><span style="color:#0468D7"> Iterable</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">ChatMessage</span><span style="color:#222222">> </span><span style="color:#BD2314">get</span><span style="color:#222222"> history;</span></span> <span class="line"><span style="color:#BD2314"> set</span><span style="color:#6200EE"> history</span><span style="color:#222222">(</span><span style="color:#0468D7">Iterable</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">ChatMessage</span><span style="color:#222222">> history);</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>When the history for a provider changes, it calls the <code>notifyListener</code> method exposed by the <code>Listenable</code> base class. This means that you manually subscribe/unsubscribe with the <code>add</code> and <code>remove</code> methods or use it to construct an instance of the <code>ListenableBuilder</code> class.</p><p>The <code>generateStream</code> method calls into the underlying LLM without affecting the history. Calling the <code>sendMessageStream</code> method changes the history by adding two new messages to the provider's history鈥攐ne for the user message and one for the LLM response鈥攚hen the response is completed. The chat view uses <code>sendMessageStream</code> when it processes a user's chat prompt and <code>generateStream</code> when it's processing the user's voice input.</p><p>To see or set the history, you can access the <code>history</code> property:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">void</span><span style="color:#6200EE"> _clearHistory</span><span style="color:#222222">() => _provider.history = [];</span></span></code></pre></div></div><p>The ability to access a provider's history is also useful when it comes to recreating a provider while maintaining the history:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> _HomePageState</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> State</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">HomePage</span><span style="color:#222222">> {</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#BD2314"> void</span><span style="color:#6200EE"> _onSettingsSave</span><span style="color:#222222">() => </span><span style="color:#6200EE">setState</span><span style="color:#222222">(() {</span></span> <span class="line"><span style="color:#6E6E70"> // move the history over from the old provider to the new one</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> history = _provider.history.</span><span style="color:#6200EE">toList</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#222222"> _provider = </span><span style="color:#6200EE">_createProvider</span><span style="color:#222222">(history);</span></span> <span class="line"><span style="color:#222222"> });</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>The <code>_createProvider</code> method creates a new provider with the history from the previous provider <em>and</em> the new user preferences. It's seamless for the user; they can keep chatting away but now the LLM gives them responses taking their new food preferences into account. For example:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> _HomePageState</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> State</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">HomePage</span><span style="color:#222222">> {</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#6E6E70"> // create a new provider with the given history and the current settings</span></span> <span class="line"><span style="color:#0468D7"> LlmProvider</span><span style="color:#6200EE"> _createProvider</span><span style="color:#222222">([</span><span style="color:#0468D7">List</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">ChatMessage</span><span style="color:#222222">>? history]) =></span></span> <span class="line"><span style="color:#0468D7"> GeminiProvider</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> history: history,</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>To see history in action, check out the <a href="https://github.com/flutter/ai/tree/main/example/lib/recipes">recipes example app</a> and the <a href="https://github.com/flutter/ai/blob/main/example/lib/history/history.dart">history example app</a>.</p><div class="header-wrapper"><h2 id="chat-serializationdeserialization">Chat serialization/deserialization</h2><a class="heading-link" href="#chat-serializationdeserialization" aria-label="Link to 'Chat serialization/deserialization' section">#</a></div><p>To save and restore chat history between sessions of an app requires the ability to serialize and deserialize each user prompt, including the attachments, and each LLM response. Both kinds of messages (the user prompts and LLM responses), are exposed in the <code>ChatMessage</code> class. Serialization can be accomplished by using the <code>toJson</code> method of each <code>ChatMessage</code> instance.</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#0468D7">Future</span><span style="color:#222222">&#x3C;</span><span style="color:#BD2314">void</span><span style="color:#222222">> </span><span style="color:#6200EE">_saveHistory</span><span style="color:#222222">() </span><span style="color:#BD2314">async</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#6E6E70"> // get the latest history</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> history = _provider.history.</span><span style="color:#6200EE">toList</span><span style="color:#222222">();</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // write the new messages</span></span> <span class="line"><span style="color:#BD2314"> for</span><span style="color:#222222"> (</span><span style="color:#BD2314">var</span><span style="color:#222222"> i = </span><span style="color:#0C7064">0</span><span style="color:#222222">; i != history.length; ++i) {</span></span> <span class="line"><span style="color:#6E6E70"> // skip if the file already exists</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> file = </span><span style="color:#BD2314">await</span><span style="color:#6200EE"> _messageFile</span><span style="color:#222222">(i);</span></span> <span class="line"><span style="color:#BD2314"> if</span><span style="color:#222222"> (file.</span><span style="color:#6200EE">existsSync</span><span style="color:#222222">()) </span><span style="color:#BD2314">continue</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // write the new message to disk</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> map = history[i].</span><span style="color:#6200EE">toJson</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> json = </span><span style="color:#0468D7">JsonEncoder</span><span style="color:#222222">.</span><span style="color:#6200EE">withIndent</span><span style="color:#222222">(</span><span style="color:#0C7064">' '</span><span style="color:#222222">).</span><span style="color:#6200EE">convert</span><span style="color:#222222">(map);</span></span> <span class="line"><span style="color:#BD2314"> await</span><span style="color:#222222"> file.</span><span style="color:#6200EE">writeAsString</span><span style="color:#222222">(json);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Likewise, to deserialize, use the static <code>fromJson</code> method of the <code>ChatMessage</code> class:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#0468D7">Future</span><span style="color:#222222">&#x3C;</span><span style="color:#BD2314">void</span><span style="color:#222222">> </span><span style="color:#6200EE">_loadHistory</span><span style="color:#222222">() </span><span style="color:#BD2314">async</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#6E6E70"> // read the history from disk</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> history = &#x3C;</span><span style="color:#0468D7">ChatMessage</span><span style="color:#222222">>[];</span></span> <span class="line"><span style="color:#BD2314"> for</span><span style="color:#222222"> (</span><span style="color:#BD2314">var</span><span style="color:#222222"> i = </span><span style="color:#0C7064">0</span><span style="color:#222222">;; ++i) {</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> file = </span><span style="color:#BD2314">await</span><span style="color:#6200EE"> _messageFile</span><span style="color:#222222">(i);</span></span> <span class="line"><span style="color:#BD2314"> if</span><span style="color:#222222"> (!file.</span><span style="color:#6200EE">existsSync</span><span style="color:#222222">()) </span><span style="color:#BD2314">break</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> map = </span><span style="color:#6200EE">jsonDecode</span><span style="color:#222222">(</span><span style="color:#BD2314">await</span><span style="color:#222222"> file.</span><span style="color:#6200EE">readAsString</span><span style="color:#222222">());</span></span> <span class="line"><span style="color:#222222"> history.</span><span style="color:#6200EE">add</span><span style="color:#222222">(</span><span style="color:#0468D7">ChatMessage</span><span style="color:#222222">.</span><span style="color:#6200EE">fromJson</span><span style="color:#222222">(map));</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // set the history on the controller</span></span> <span class="line"><span style="color:#222222"> _provider.history = history;</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>To ensure fast turnaround when serializing, we recommend only writing each user message once. Otherwise, the user must wait for your app to write every message every time and, in the face of binary attachments, that could take a while.</p><p>To see this in action, check out the <a href="https://github.com/flutter/ai/blob/main/example/lib/history/history.dart">history example app</a>.</p><div class="header-wrapper"><h2 id="custom-response-widgets">Custom response widgets</h2><a class="heading-link" href="#custom-response-widgets" aria-label="Link to 'Custom response widgets' section">#</a></div><p>By default, the LLM response shown by the chat view is formatted Markdown. However, in some cases, you want to create a custom widget to show the LLM response that's specific to and integrated with your app. For example, when the user requests a recipe in the <a href="https://github.com/flutter/ai/tree/main/example/lib/recipes">recipes example app</a>, the LLM response is used to create a widget that's specific to showing recipes just like the rest of the app does and to provide for an <strong>Add</strong> button in case the user would like to add the recipe to their database:</p><p><img src="/assets/images/docs/ai-toolkit/add-recipe-button.png" alt="Add recipe button"></p><p>This is accomplished by setting the <code>responseBuilder</code> parameter of the <code>LlmChatView</code> constructor:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#0468D7">LlmChatView</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> provider: _provider,</span></span> <span class="line"><span style="color:#222222"> welcomeMessage: _welcomeMessage,</span></span> <span class="line"><span style="color:#222222"> responseBuilder: (context, response) => </span><span style="color:#0468D7">RecipeResponseView</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> response,</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222">),</span></span></code></pre></div></div><p>In this particular example, the <code>RecipeReponseView</code> widget is constructed with the LLM provider's response text and uses that to implement its <code>build</code> method:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> RecipeResponseView</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> StatelessWidget</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> const</span><span style="color:#0468D7"> RecipeResponseView</span><span style="color:#222222">(</span><span style="color:#BD2314">this</span><span style="color:#222222">.response, {</span><span style="color:#BD2314">super</span><span style="color:#222222">.key});</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#0468D7"> String</span><span style="color:#222222"> response;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> @override</span></span> <span class="line"><span style="color:#0468D7"> Widget</span><span style="color:#6200EE"> build</span><span style="color:#222222">(</span><span style="color:#0468D7">BuildContext</span><span style="color:#222222"> context) {</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> children = &#x3C;</span><span style="color:#0468D7">Widget</span><span style="color:#222222">>[];</span></span> <span class="line"><span style="color:#0468D7"> String</span><span style="color:#222222">? finalText;</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // created with the response from the LLM as the response streams in, so</span></span> <span class="line"><span style="color:#6E6E70"> // many not be a complete response yet</span></span> <span class="line"><span style="color:#BD2314"> try</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> map = </span><span style="color:#6200EE">jsonDecode</span><span style="color:#222222">(response);</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> recipesWithText = map[</span><span style="color:#0C7064">'recipes'</span><span style="color:#222222">] </span><span style="color:#BD2314">as</span><span style="color:#0468D7"> List</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">dynamic</span><span style="color:#222222">>;</span></span> <span class="line"><span style="color:#222222"> finalText = map[</span><span style="color:#0C7064">'text'</span><span style="color:#222222">] </span><span style="color:#BD2314">as</span><span style="color:#0468D7"> String</span><span style="color:#222222">?;</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> for</span><span style="color:#222222"> (</span><span style="color:#BD2314">final</span><span style="color:#222222"> recipeWithText </span><span style="color:#BD2314">in</span><span style="color:#222222"> recipesWithText) {</span></span> <span class="line"><span style="color:#6E6E70"> // extract the text before the recipe</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> text = recipeWithText[</span><span style="color:#0C7064">'text'</span><span style="color:#222222">] </span><span style="color:#BD2314">as</span><span style="color:#0468D7"> String</span><span style="color:#222222">?;</span></span> <span class="line"><span style="color:#BD2314"> if</span><span style="color:#222222"> (text != </span><span style="color:#0C7064">null</span><span style="color:#222222"> &#x26;&#x26; text.isNotEmpty) {</span></span> <span class="line"><span style="color:#222222"> children.</span><span style="color:#6200EE">add</span><span style="color:#222222">(</span><span style="color:#0468D7">MarkdownBody</span><span style="color:#222222">(data: text));</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // extract the recipe</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> json = recipeWithText[</span><span style="color:#0C7064">'recipe'</span><span style="color:#222222">] </span><span style="color:#BD2314">as</span><span style="color:#0468D7"> Map</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">String</span><span style="color:#222222">, </span><span style="color:#0468D7">dynamic</span><span style="color:#222222">>;</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> recipe = </span><span style="color:#0468D7">Recipe</span><span style="color:#222222">.</span><span style="color:#6200EE">fromJson</span><span style="color:#222222">(json);</span></span> <span class="line"><span style="color:#222222"> children.</span><span style="color:#6200EE">add</span><span style="color:#222222">(</span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Gap</span><span style="color:#222222">(</span><span style="color:#0C7064">16</span><span style="color:#222222">));</span></span> <span class="line"><span style="color:#222222"> children.</span><span style="color:#6200EE">add</span><span style="color:#222222">(</span><span style="color:#0468D7">Column</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> crossAxisAlignment: </span><span style="color:#0468D7">CrossAxisAlignment</span><span style="color:#222222">.start,</span></span> <span class="line"><span style="color:#222222"> children: [</span></span> <span class="line"><span style="color:#0468D7"> Text</span><span style="color:#222222">(recipe.title, style: </span><span style="color:#0468D7">Theme</span><span style="color:#222222">.</span><span style="color:#6200EE">of</span><span style="color:#222222">(context).textTheme.titleLarge),</span></span> <span class="line"><span style="color:#0468D7"> Text</span><span style="color:#222222">(recipe.description),</span></span> <span class="line"><span style="color:#0468D7"> RecipeContentView</span><span style="color:#222222">(recipe: recipe),</span></span> <span class="line"><span style="color:#222222"> ],</span></span> <span class="line"><span style="color:#222222"> ));</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // add a button to add the recipe to the list</span></span> <span class="line"><span style="color:#222222"> children.</span><span style="color:#6200EE">add</span><span style="color:#222222">(</span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Gap</span><span style="color:#222222">(</span><span style="color:#0C7064">16</span><span style="color:#222222">));</span></span> <span class="line"><span style="color:#222222"> children.</span><span style="color:#6200EE">add</span><span style="color:#222222">(</span><span style="color:#0468D7">OutlinedButton</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> onPressed: () => </span><span style="color:#0468D7">RecipeRepository</span><span style="color:#222222">.</span><span style="color:#6200EE">addNewRecipe</span><span style="color:#222222">(recipe),</span></span> <span class="line"><span style="color:#222222"> child: </span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Text</span><span style="color:#222222">(</span><span style="color:#0C7064">'Add Recipe'</span><span style="color:#222222">),</span></span> <span class="line"><span style="color:#222222"> ));</span></span> <span class="line"><span style="color:#222222"> children.</span><span style="color:#6200EE">add</span><span style="color:#222222">(</span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Gap</span><span style="color:#222222">(</span><span style="color:#0C7064">16</span><span style="color:#222222">));</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> } </span><span style="color:#BD2314">catch</span><span style="color:#222222"> (e) {</span></span> <span class="line"><span style="color:#6200EE"> debugPrint</span><span style="color:#222222">(</span><span style="color:#0C7064">'Error parsing response: </span><span style="color:#0C7064">$</span><span style="color:#222222">e</span><span style="color:#0C7064">'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> return</span><span style="color:#0468D7"> Column</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> crossAxisAlignment: </span><span style="color:#0468D7">CrossAxisAlignment</span><span style="color:#222222">.start,</span></span> <span class="line"><span style="color:#222222"> children: children,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>This code parses the text to extract introductory text and the recipe from the LLM, bundling them together with an <strong>Add Recipe</strong> button to show in place of the Markdown.</p><p>Notice that we're parsing the LLM response as JSON. It's common to set the provider into JSON mode and to provide a schema to restrict the format of its responses to ensure that we've got something we can parse. Each provider exposes this functionality in its own way, but both the <code>GeminiProvider</code> and <code>VertexProvider</code> classes enable this with a <code>GenerationConfig</code> object that the recipes example uses as follows:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> _HomePageState</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> State</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">HomePage</span><span style="color:#222222">> {</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // create a new provider with the given history and the current settings</span></span> <span class="line"><span style="color:#0468D7"> LlmProvider</span><span style="color:#6200EE"> _createProvider</span><span style="color:#222222">([</span><span style="color:#0468D7">List</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">ChatMessage</span><span style="color:#222222">>? history]) => </span><span style="color:#0468D7">GeminiProvider</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#222222"> model: </span><span style="color:#0468D7">GenerativeModel</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#222222"> generationConfig: </span><span style="color:#0468D7">GenerationConfig</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> responseMimeType: </span><span style="color:#0C7064">'application/json'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> responseSchema: </span><span style="color:#0468D7">Schema</span><span style="color:#222222">(...),</span></span> <span class="line"><span style="color:#222222"> systemInstruction: </span><span style="color:#0468D7">Content</span><span style="color:#222222">.</span><span style="color:#6200EE">system</span><span style="color:#222222">(</span><span style="color:#0C7064">'''</span></span> <span class="line"><span style="color:#0C7064">...</span></span> <span class="line"><span style="color:#0C7064">Generate each response in JSON format</span></span> <span class="line"><span style="color:#0C7064">with the following schema, including one or more "text" and "recipe" pairs as</span></span> <span class="line"><span style="color:#0C7064">well as any trailing text commentary you care to provide:</span></span> <span class="line"></span> <span class="line"><span style="color:#0C7064">{</span></span> <span class="line"><span style="color:#0C7064"> "recipes": [</span></span> <span class="line"><span style="color:#0C7064"> {</span></span> <span class="line"><span style="color:#0C7064"> "text": "Any commentary you care to provide about the recipe.",</span></span> <span class="line"><span style="color:#0C7064"> "recipe":</span></span> <span class="line"><span style="color:#0C7064"> {</span></span> <span class="line"><span style="color:#0C7064"> "title": "Recipe Title",</span></span> <span class="line"><span style="color:#0C7064"> "description": "Recipe Description",</span></span> <span class="line"><span style="color:#0C7064"> "ingredients": ["Ingredient 1", "Ingredient 2", "Ingredient 3"],</span></span> <span class="line"><span style="color:#0C7064"> "instructions": ["Instruction 1", "Instruction 2", "Instruction 3"]</span></span> <span class="line"><span style="color:#0C7064"> }</span></span> <span class="line"><span style="color:#0C7064"> }</span></span> <span class="line"><span style="color:#0C7064"> ],</span></span> <span class="line"><span style="color:#0C7064"> "text": "any final commentary you care to provide",</span></span> <span class="line"><span style="color:#0C7064">}</span></span> <span class="line"><span style="color:#0C7064">'''</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>This code initializes the <code>GenerationConfig</code> object by setting the <code>responseMimeType</code> parameter to <code>'application/json'</code> and the <code>responseSchema</code> parameter to an instance of the <code>Schema</code> class that defines the structure of the JSON that you're prepared to parse. In addition, it's good practice to also ask for JSON and to provide a description of that JSON schema in the system instructions, which we've done here.</p><p>To see this in action, check out the <a href="https://github.com/flutter/ai/tree/main/example/lib/recipes">recipes example app</a>.</p><div class="header-wrapper"><h2 id="custom-styling">Custom styling</h2><a class="heading-link" href="#custom-styling" aria-label="Link to 'Custom styling' section">#</a></div><p>The chat view comes out of the box with a set of default styles for the background, the text field, the buttons, the icons, the suggestions, and so on. You can fully customize those styles by setting your own by using the <code>style</code> parameter to the <code>LlmChatView</code> constructor:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#0468D7">LlmChatView</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> provider: </span><span style="color:#0468D7">GeminiProvider</span><span style="color:#222222">(...),</span></span> <span class="line"><span style="color:#222222"> style: </span><span style="color:#0468D7">LlmChatViewStyle</span><span style="color:#222222">(...),</span></span> <span class="line"><span style="color:#222222">),</span></span></code></pre></div></div><p>For example, the <a href="https://github.com/flutter/ai/blob/main/example/lib/custom_styles/custom_styles.dart">custom styles example app</a> uses this feature to implement an app with a Halloween theme:</p><p><img src="/assets/images/docs/ai-toolkit/demo-app.png" alt="Halloween-themed demo app"></p><p>For a complete list of the styles available in the <code>LlmChatViewStyle</code> class, check out the <a href="https://pub.dev/documentation/flutter_ai_toolkit/latest/flutter_ai_toolkit/LlmChatViewStyle-class.html">reference documentation</a>. To see custom styles in action, in addition to the <a href="https://github.com/flutter/ai/blob/main/example/lib/custom_styles/custom_styles.dart">custom styles example</a>, check out the <a href="https://github.com/flutter/ai/blob/main/example/lib/dark_mode/dark_mode.dart">dark mode example</a> and the <a href="https://github.com/flutter/ai#online-demo">demo app</a>.</p><div class="header-wrapper"><h2 id="chat-without-ui">Chat without UI</h2><a class="heading-link" href="#chat-without-ui" aria-label="Link to 'Chat without UI' section">#</a></div><p>You don't have to use the chat view to access the functionality of the underlying provider. In addition to being able to simply call it with whatever proprietary interface it provides, you can also use it with the <a href="https://pub.dev/documentation/flutter_ai_toolkit/latest/flutter_ai_toolkit/LlmProvider-class.html">LlmProvider interface</a>.</p><p>As an example, the recipes example app provides a Magic button on the page for editing recipes. The purpose of that button is to update an existing recipe in your database with your current food preferences. Pressing the button allows you to preview the recommended changes and decide whether you'd like to apply them or not:</p><p><img src="/assets/images/docs/ai-toolkit/apply-changes-decision.png" alt="User decides whether to update recipe in database"></p><p>Instead of using the same provider that the chat portion of the app uses, which would insert spurious user messages and LLM responses into the user's chat history, the Edit Recipe page instead creates its own provider and uses it directly:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> _EditRecipePageState</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> State</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">EditRecipePage</span><span style="color:#222222">> {</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> _provider = </span><span style="color:#0468D7">GeminiProvider</span><span style="color:#222222">(...);</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#0468D7"> Future</span><span style="color:#222222">&#x3C;</span><span style="color:#BD2314">void</span><span style="color:#222222">> </span><span style="color:#6200EE">_onMagic</span><span style="color:#222222">() </span><span style="color:#BD2314">async</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> stream = _provider.</span><span style="color:#6200EE">sendMessageStream</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0C7064"> 'Generate a modified version of this recipe based on my food preferences: '</span></span> <span class="line"><span style="color:#0C7064"> '</span><span style="color:#0C7064">${</span><span style="color:#222222">_ingredientsController</span><span style="color:#0C7064">.</span><span style="color:#222222">text</span><span style="color:#0C7064">}</span><span style="color:#222222">\n\n</span><span style="color:#0C7064">${</span><span style="color:#222222">_instructionsController</span><span style="color:#0C7064">.</span><span style="color:#222222">text</span><span style="color:#0C7064">}</span><span style="color:#0C7064">'</span><span style="color:#222222">,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#BD2314"> var</span><span style="color:#222222"> response = </span><span style="color:#BD2314">await</span><span style="color:#222222"> stream.</span><span style="color:#6200EE">join</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> json = </span><span style="color:#6200EE">jsonDecode</span><span style="color:#222222">(response);</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> try</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> modifications = json[</span><span style="color:#0C7064">'modifications'</span><span style="color:#222222">];</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> recipe = </span><span style="color:#0468D7">Recipe</span><span style="color:#222222">.</span><span style="color:#6200EE">fromJson</span><span style="color:#222222">(json[</span><span style="color:#0C7064">'recipe'</span><span style="color:#222222">]);</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> if</span><span style="color:#222222"> (!context.mounted) </span><span style="color:#BD2314">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> accept = </span><span style="color:#BD2314">await</span><span style="color:#6200EE"> showDialog</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">bool</span><span style="color:#222222">>(</span></span> <span class="line"><span style="color:#222222"> context: context,</span></span> <span class="line"><span style="color:#222222"> builder: (context) => </span><span style="color:#0468D7">AlertDialog</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> title: </span><span style="color:#0468D7">Text</span><span style="color:#222222">(recipe.title),</span></span> <span class="line"><span style="color:#222222"> content: </span><span style="color:#0468D7">Column</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> crossAxisAlignment: </span><span style="color:#0468D7">CrossAxisAlignment</span><span style="color:#222222">.start,</span></span> <span class="line"><span style="color:#222222"> children: [</span></span> <span class="line"><span style="color:#BD2314"> const</span><span style="color:#0468D7"> Text</span><span style="color:#222222">(</span><span style="color:#0C7064">'Modifications:'</span><span style="color:#222222">),</span></span> <span class="line"><span style="color:#BD2314"> const</span><span style="color:#0468D7"> Gap</span><span style="color:#222222">(</span><span style="color:#0C7064">16</span><span style="color:#222222">),</span></span> <span class="line"><span style="color:#0468D7"> Text</span><span style="color:#222222">(</span><span style="color:#6200EE">_wrapText</span><span style="color:#222222">(modifications)),</span></span> <span class="line"><span style="color:#222222"> ],</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> actions: [</span></span> <span class="line"><span style="color:#0468D7"> TextButton</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> onPressed: () => context.</span><span style="color:#6200EE">pop</span><span style="color:#222222">(</span><span style="color:#0C7064">true</span><span style="color:#222222">),</span></span> <span class="line"><span style="color:#222222"> child: </span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Text</span><span style="color:#222222">(</span><span style="color:#0C7064">'Accept'</span><span style="color:#222222">),</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#0468D7"> TextButton</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> onPressed: () => context.</span><span style="color:#6200EE">pop</span><span style="color:#222222">(</span><span style="color:#0C7064">false</span><span style="color:#222222">),</span></span> <span class="line"><span style="color:#222222"> child: </span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Text</span><span style="color:#222222">(</span><span style="color:#0C7064">'Reject'</span><span style="color:#222222">),</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> ],</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#222222"> } </span><span style="color:#BD2314">catch</span><span style="color:#222222"> (ex) {</span></span> <span class="line"><span style="color:#222222"> ...</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>The call to <code>sendMessageStream</code> creates entries in the provider's history, but since it's not associated with a chat view, they won't be shown. If it's convenient, you can also accomplish the same thing by calling <code>generateStream</code>, which allows you to reuse an existing provider without affecting the chat history.</p><p>To see this in action, check out the <a href="https://github.com/flutter/ai/blob/main/example/lib/recipes/pages/edit_recipe_page.dart">Edit Recipe page</a> of the recipes example.</p><div class="header-wrapper"><h2 id="rerouting-prompts">Rerouting prompts</h2><a class="heading-link" href="#rerouting-prompts" aria-label="Link to 'Rerouting prompts' section">#</a></div><p>If you'd like to debug, log, or manipulate the connection between the chat view and the underlying provider, you can do so with an implementation of an <a href="https://pub.dev/documentation/flutter_ai_toolkit/latest/flutter_ai_toolkit/LlmStreamGenerator.html"><code>LlmStreamGenerator</code></a> function. You then pass that function to the <code>LlmChatView</code> in the <code>messageSender</code> parameter:</p><div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#BD2314">class</span><span style="color:#0468D7"> ChatPage</span><span style="color:#BD2314"> extends</span><span style="color:#0468D7"> StatelessWidget</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> _provider = </span><span style="color:#0468D7">GeminiProvider</span><span style="color:#222222">(...);</span></span> <span class="line"></span> <span class="line"><span style="color:#BD2314"> @override</span></span> <span class="line"><span style="color:#0468D7"> Widget</span><span style="color:#6200EE"> build</span><span style="color:#222222">(</span><span style="color:#0468D7">BuildContext</span><span style="color:#222222"> context) => </span><span style="color:#0468D7">Scaffold</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> appBar: </span><span style="color:#0468D7">AppBar</span><span style="color:#222222">(title: </span><span style="color:#BD2314">const</span><span style="color:#0468D7"> Text</span><span style="color:#222222">(</span><span style="color:#0468D7">App</span><span style="color:#222222">.title)),</span></span> <span class="line"><span style="color:#222222"> body: </span><span style="color:#0468D7">LlmChatView</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> provider: _provider,</span></span> <span class="line"><span style="color:#222222"> messageSender: _logMessage,</span></span> <span class="line"><span style="color:#222222"> ),</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"></span> <span class="line"><span style="color:#0468D7"> Stream</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">String</span><span style="color:#222222">> </span><span style="color:#6200EE">_logMessage</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#0468D7"> String</span><span style="color:#222222"> prompt, {</span></span> <span class="line"><span style="color:#BD2314"> required</span><span style="color:#0468D7"> Iterable</span><span style="color:#222222">&#x3C;</span><span style="color:#0468D7">Attachment</span><span style="color:#222222">> attachments,</span></span> <span class="line"><span style="color:#222222"> }) </span><span style="color:#BD2314">async</span><span style="color:#222222">* {</span></span> <span class="line"><span style="color:#6E6E70"> // log the message and attachments</span></span> <span class="line"><span style="color:#6200EE"> debugPrint</span><span style="color:#222222">(</span><span style="color:#0C7064">'# Sending Message'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#6200EE"> debugPrint</span><span style="color:#222222">(</span><span style="color:#0C7064">'## Prompt</span><span style="color:#222222">\n</span><span style="color:#0C7064">$</span><span style="color:#222222">prompt</span><span style="color:#0C7064">'</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#6200EE"> debugPrint</span><span style="color:#222222">(</span><span style="color:#0C7064">'## Attachments</span><span style="color:#222222">\n</span><span style="color:#0C7064">${</span><span style="color:#222222">attachments</span><span style="color:#0C7064">.</span><span style="color:#6200EE">map</span><span style="color:#0C7064">((</span><span style="color:#222222">a</span><span style="color:#0C7064">) => </span><span style="color:#222222">a</span><span style="color:#0C7064">.</span><span style="color:#6200EE">toString</span><span style="color:#0C7064">())}</span><span style="color:#0C7064">'</span><span style="color:#222222">);</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // forward the message on to the provider</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> response = _provider.</span><span style="color:#6200EE">sendMessageStream</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> prompt,</span></span> <span class="line"><span style="color:#222222"> attachments: attachments,</span></span> <span class="line"><span style="color:#222222"> );</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // log the response</span></span> <span class="line"><span style="color:#BD2314"> final</span><span style="color:#222222"> text = </span><span style="color:#BD2314">await</span><span style="color:#222222"> response.</span><span style="color:#6200EE">join</span><span style="color:#222222">();</span></span> <span class="line"><span style="color:#6200EE"> debugPrint</span><span style="color:#222222">(</span><span style="color:#0C7064">'## Response</span><span style="color:#222222">\n</span><span style="color:#0C7064">$</span><span style="color:#222222">text</span><span style="color:#0C7064">'</span><span style="color:#222222">);</span></span> <span class="line"></span> <span class="line"><span style="color:#6E6E70"> // return it</span></span> <span class="line"><span style="color:#BD2314"> yield</span><span style="color:#222222"> text;</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>This example logs the user prompts and LLM responses as they go back and forth. When providing a function as a <code>messageSender</code>, it's your responsibility to call the underlying provider. If you don't, it won't get the message. This capability allows you to do advanced things like routing to a provider dynamically or Retrieval Augmented Generation (RAG).</p><p>To see this in action, check out the <a href="https://github.com/flutter/ai/blob/main/example/lib/logging/logging.dart">logging example app</a>.</p><nav class="site-nextprev-nav"><ul><li class="prev"><a href="/ai-toolkit/user-experience">User experience</a></li><li class="next"><a href="/ai-toolkit/custom-llm-providers">Custom LLM providers</a></li></ul></nav><p id="page-github-links"><span>Unless stated otherwise, the documentation on this site reflects the latest stable version of Flutter. Page last updated on 2024-12-17.</span> <a href="https://github.com/flutter/website/tree/main/src/content/ai-toolkit/feature-integration.md" target="_blank" rel="noopener">View source</a> <span>or </span><a href="https://github.com/flutter/website/issues/new?template=1_page_issue.yml&&page-url=https://docs.flutter.dev/ai-toolkit/feature-integration/&page-source=https://github.com/flutter/website/tree/main/src/content/ai-toolkit/feature-integration.md" title="Report an issue with this page" target="_blank" rel="noopener">report an issue</a>.</p></article></main></div><footer id="site-footer"><div class="footer-section footer-main"><a class="brand" href="https://flutter.dev"><img src="/assets/images/branding/flutter/logo+text/horizontal/white.svg" alt="Flutter logo" width="164"></a><div class="footer-social-links"><a class="icon-button" href="https://medium.com/flutter" target="_blank" rel="noopener" title="Flutter's Medium blog"><svg><use href="/assets/images/social/medium.svg#medium"></use></svg> </a><a class="icon-button" href="https://youtube.com/@flutterdev" target="_blank" rel="noopener" title="Flutter's YouTube"><svg><use href="/assets/images/social/youtube.svg#youtube"></use></svg> </a><a class="icon-button" href="https://github.com/flutter" target="_blank" rel="noopener" title="Flutter's GitHub"><svg><use href="/assets/images/social/github.svg#github"></use></svg> </a><a class="icon-button" href="https://bsky.app/profile/flutter.dev" target="_blank" rel="noopener" title="Flutter's Bluesky"><svg><use href="/assets/images/social/bluesky.svg#bluesky"></use></svg> </a><a class="icon-button" href="https://twitter.com/FlutterDev" target="_blank" rel="noopener" title="Flutter's X (Twitter)"><svg><use href="/assets/images/social/x.svg#x"></use></svg></a></div></div><div class="footer-section footer-tray"><div class="footer-licenses">Except as otherwise noted, this site is licensed under a <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>, and code samples are licensed under the <a href="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</a>.</div><div class="footer-utility-links"><ul><li><a href="/tos" title="Terms of use">Terms</a></li><li><a href="/brand" title="Brand usage guidelines">Brand</a></li><li><a href="https://policies.google.com/privacy" target="_blank" rel="noopener" title="Privacy policy">Privacy</a></li><li><a href="/security" title="Security philosophy and practices">Security</a></li></ul></div></div></footer></div><script src="/assets/js/tabs.js?v=4"></script><script src="/assets/js/archive.js?v=4"></script><script src="/assets/js/main.js?v=4"></script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10