<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Rody Davis</title><description>Rody Davis&apos;s personal website and portfolio.</description><link>https://rodydavis.com/</link><language>en-us</language><item><title>About Me</title><link>https://rodydavis.com/about/</link><guid isPermaLink="true">https://rodydavis.com/about/</guid><description>Rody Davis is a Senior Developer Advocate at Google specializing in Antigravity and Web Components, who also works as a musician, podcast creator, and developer advocate.</description><pubDate>Sat, 18 Jan 2025 19:18:39 GMT</pubDate><content:encoded>&lt;p&gt;import profileImg from &apos;../../assets/profile_8v0z7aduoc.webp&apos;;
import grandCanyonImg from &apos;../../assets/personal_grand_canyon_5wbn12eol9.webp&apos;;
import shuttleImg from &apos;../../assets/personal_shuttle_o0dk6jemz6.webp&apos;;
import goldenGateImg from &apos;../../assets/personal_golden_gate_9y3fwiwicv.webp&apos;;
import familyImg from &apos;../../assets/personal_family_qxtsx99czk.webp&apos;;
import rodyMollyImg from &apos;../../assets/riyakataria_rody_molly_031823_20_1_r5q7mluc1z.jpg&apos;;&lt;/p&gt;
&lt;h1&gt;About Me&lt;/h1&gt;
&lt;p&gt;&amp;lt;p class=&amp;quot;text-md text-[var(--header-h1)] font-semibold -mt-4 mb-8&amp;quot;&amp;gt;Rody Davis — Career &amp;amp; Passions&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&amp;quot;flex flex-col md:flex-row gap-8 items-start mb-12&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;w-full md:w-1/3&amp;quot;&amp;gt;
&amp;lt;img src={profileImg.src} alt=&amp;quot;Rody Davis Portrait&amp;quot; class=&amp;quot;w-full rounded-xl border border-[var(--border-main)] shadow-sm bg-[#13151a]&amp;quot; /&amp;gt;
&amp;lt;div class=&amp;quot;bg-[var(--bg-sidebar)] border border-[var(--border-main)] rounded-xl p-4 mt-6&amp;quot;&amp;gt;
&amp;lt;h4 class=&amp;quot;text-xs font-bold uppercase tracking-wider text-[var(--fg-secondary)] mt-0 mb-3&amp;quot;&amp;gt;⚡ Quick Facts&amp;lt;/h4&amp;gt;
&amp;lt;ul class=&amp;quot;space-y-2 text-xs text-[var(--fg-secondary)] list-none pl-0 m-0&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;💼 &amp;lt;strong&amp;gt;Current Role:&amp;lt;/strong&amp;gt; Senior DevRel at Google&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;📍 &amp;lt;strong&amp;gt;Location:&amp;lt;/strong&amp;gt; San Francisco Bay Area&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🎓 &amp;lt;strong&amp;gt;Education:&amp;lt;/strong&amp;gt; Florida College (B.S. in Management Information Systems, minor in Business Administration)&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🎹 &amp;lt;strong&amp;gt;Secondary Passion:&amp;lt;/strong&amp;gt; Synthesizers &amp;amp; Sound Synthesis&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;w-full md:w-2/3 text-[var(--fg-secondary)] text-md leading-relaxed&amp;quot;&amp;gt;
&amp;lt;p class=&amp;quot;m-0 mb-4&amp;quot;&amp;gt;
My name is Rody Davis. I grew up in Alabama, studied Management Information Systems at Florida College, and currently work at Google as a Senior Developer Relations Engineer, living in the beautiful San Francisco Bay Area.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&amp;quot;m-0 mb-4&amp;quot;&amp;gt;
Ever since I was a child, I was taking apart every device I could get my hands on to figure out how it worked, always aspiring to be an inventor. In high school, I spent my free time on the drumline in marching band, learning rhythm, coordination, and cooperative execution.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&amp;quot;m-0 mb-4&amp;quot;&amp;gt;
Today, I get to do my dream job. I am an inventor—bringing highly interactive products from pure imagination to market, building frameworks, and championing the developer journey.
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;🗺️ Chronological Journey: Career &amp;amp; Personal&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&amp;quot;space-y-12 pl-4 border-l-2 border-[var(--border-main)] mt-8&amp;quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;{/* Chapter 1 */}
&amp;lt;div class=&amp;quot;relative pl-6&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;absolute -left-[27px] top-1.5 w-3 h-3 rounded-full bg-sky-500 border-4 border-[#13151a]&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;span class=&amp;quot;text-xs font-bold uppercase tracking-wider text-sky-500&amp;quot;&amp;gt;2012 – 2017&amp;lt;/span&amp;gt;
&amp;lt;h3 class=&amp;quot;text-lg font-bold text-[var(--fg-primary)] mt-1 mb-3&amp;quot;&amp;gt;Chapter 1: Creative Audio &amp;amp; Technical Roots&amp;lt;/h3&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-4&amp;quot;&amp;gt;
I attended Florida College to earn a Bachelor of Science in Management Information Systems with a minor in Business Administration, originally planning to build a career as an Audio-Visual Technician. Over five years, I managed large video production environments, became a System Administrator, and eventually led the video department for a growing startup.
&amp;lt;/p&amp;gt;
&amp;lt;div class=&amp;quot;my-6&amp;quot;&amp;gt;
&amp;lt;img src={grandCanyonImg.src} alt=&amp;quot;Grand Canyon Exploration&amp;quot; class=&amp;quot;w-full rounded-xl border border-[var(--border-main)] shadow-sm bg-[#13151a]&amp;quot; /&amp;gt;
&amp;lt;p class=&amp;quot;text-xs text-center text-[var(--fg-secondary)] mt-2&amp;quot;&amp;gt;Exploring national landscapes and planning technical goals&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;{/* Chapter 2 */}
&amp;lt;div class=&amp;quot;relative pl-6&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;absolute -left-[27px] top-1.5 w-3 h-3 rounded-full bg-emerald-500 border-4 border-[#13151a]&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;span class=&amp;quot;text-xs font-bold uppercase tracking-wider text-emerald-500&amp;quot;&amp;gt;2017 – 2019&amp;lt;/span&amp;gt;
&amp;lt;h3 class=&amp;quot;text-lg font-bold text-[var(--fg-primary)] mt-1 mb-3&amp;quot;&amp;gt;Chapter 2: Self-Taught App Development &amp;amp; &amp;quot;The Pitch Pipe&amp;quot;&amp;lt;/h3&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-4&amp;quot;&amp;gt;
Realizing video management wasn&apos;t my final destination, I spent my nights teaching myself mobile software engineering. To solve an overlooked gap in the market, I created and shipped my very first mobile app, &lt;strong&gt;&amp;quot;The Pitch Pipe&amp;quot;&lt;/strong&gt;. It quickly gained massive traction among musicians, opening doors for me to transition into a professional full-time developer.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-4&amp;quot;&amp;gt;
Soon after, I joined &lt;strong&gt;CrossMedia LLC&lt;/strong&gt; as a Full Stack Developer, where I built native iOS and Android utility applications, engineered mobile REST APIs, integrated SOC-III compliant Firebase backends, and ran mobile departments.
&amp;lt;/p&amp;gt;
&amp;lt;div class=&amp;quot;my-6&amp;quot;&amp;gt;
&amp;lt;img src={shuttleImg.src} alt=&amp;quot;Inspiration from Engineering and Flight&amp;quot; class=&amp;quot;w-full rounded-xl border border-[var(--border-main)] shadow-sm bg-[#13151a]&amp;quot; /&amp;gt;
&amp;lt;p class=&amp;quot;text-xs text-center text-[var(--fg-secondary)] mt-2&amp;quot;&amp;gt;Driven by structural mechanics and high-performance execution&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;{/* Chapter 3 */}
&amp;lt;div class=&amp;quot;relative pl-6&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;absolute -left-[27px] top-1.5 w-3 h-3 rounded-full bg-purple-500 border-4 border-[#13151a]&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;span class=&amp;quot;text-xs font-bold uppercase tracking-wider text-purple-500&amp;quot;&amp;gt;2019 – 2020&amp;lt;/span&amp;gt;
&amp;lt;h3 class=&amp;quot;text-lg font-bold text-[var(--fg-primary)] mt-1 mb-3&amp;quot;&amp;gt;Chapter 3: Interactive Runtimes at Rive&amp;lt;/h3&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-4&amp;quot;&amp;gt;
I joined &lt;strong&gt;Rive&lt;/strong&gt; as a Software Engineer in San Francisco, focusing heavily on interactive rendering pipelines. I built Next.js Flutter editor prototypes on top of CanvasKit, developed the core Android animation runtimes in Kotlin utilizing native SKIA graphics APIs, and authored modular desktop plugins using C++ and Swift for macOS, Windows, and Linux.
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;{/* Chapter 4 */}
&amp;lt;div class=&amp;quot;relative pl-6&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;absolute -left-[27px] top-1.5 w-3 h-3 rounded-full bg-pink-500 border-4 border-[#13151a]&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;span class=&amp;quot;text-xs font-bold uppercase tracking-wider text-pink-500&amp;quot;&amp;gt;2020 – Present&amp;lt;/span&amp;gt;
&amp;lt;h3 class=&amp;quot;text-lg font-bold text-[var(--fg-primary)] mt-1 mb-3&amp;quot;&amp;gt;Chapter 4: Developer Advocacy &amp;amp; Platforms at Google&amp;lt;/h3&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-4&amp;quot;&amp;gt;
In July 2020, I joined Google as a Senior Developer Relations Engineer. Over the last several years, I have successfully supported multiple high-profile platform initiatives:
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;grid grid-cols-1 md:grid-cols-2 gap-6 my-6&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;bg-[var(--bg-sidebar)] border border-[var(--border-main)] rounded-lg p-5&amp;quot;&amp;gt;
    &amp;lt;h4 class=&amp;quot;text-sm font-bold text-[var(--fg-primary)] mt-0 mb-2 flex items-center gap-2&amp;quot;&amp;gt;🕹️ Antigravity&amp;lt;/h4&amp;gt;
    &amp;lt;p class=&amp;quot;text-xs text-[var(--fg-secondary)] leading-relaxed mb-0&amp;quot;&amp;gt;
      Orchestrated the product launch of Antigravity on November 1, 2025. Spearheaded technical 100-300 level documentation, produced highly-engaging video segments, designed Gemini 3 launch day simulations, and shaped cross-functional partnerships with Firebase and Flutter.
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class=&amp;quot;bg-[var(--bg-sidebar)] border border-[var(--border-main)] rounded-lg p-5&amp;quot;&amp;gt;
    &amp;lt;h4 class=&amp;quot;text-sm font-bold text-[var(--fg-primary)] mt-0 mb-2 flex items-center gap-2&amp;quot;&amp;gt;🧱 Project IDX &amp;amp; Firebase Studio&amp;lt;/h4&amp;gt;
    &amp;lt;p class=&amp;quot;text-xs text-[var(--fg-secondary)] leading-relaxed mb-0&amp;quot;&amp;gt;
      Launched Firebase Studio at Cloud Next 2025, ran large technical demonstrations at Google I/O events, created templates for Gemini interactions, and led community migration plans towards Antigravity and AI Studio.
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class=&amp;quot;bg-[var(--bg-sidebar)] border border-[var(--border-main)] rounded-lg p-5&amp;quot;&amp;gt;
    &amp;lt;h4 class=&amp;quot;text-sm font-bold text-[var(--fg-primary)] mt-0 mb-2 flex items-center gap-2&amp;quot;&amp;gt;🎨 Material Design &amp;amp; Figma&amp;lt;/h4&amp;gt;
    &amp;lt;p class=&amp;quot;text-xs text-[var(--fg-secondary)] leading-relaxed mb-0&amp;quot;&amp;gt;
      Supported Lit and Material Web Components. Designed the &amp;lt;strong&amp;gt;Material Theme Builder&amp;lt;/strong&amp;gt; Figma plugin, scaling it successfully to **400,000+ active creators**. Handled multi-platform client libraries (MCU) across Swift, C, JS, and Dart.
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class=&amp;quot;bg-[var(--bg-sidebar)] border border-[var(--border-main)] rounded-lg p-5&amp;quot;&amp;gt;
    &amp;lt;h4 class=&amp;quot;text-sm font-bold text-[var(--fg-primary)] mt-0 mb-2 flex items-center gap-2&amp;quot;&amp;gt;🚀 Developer Solutions&amp;lt;/h4&amp;gt;
    &amp;lt;p class=&amp;quot;text-xs text-[var(--fg-secondary)] leading-relaxed mb-0&amp;quot;&amp;gt;
      Collaborated across Flutter, Go, Angular, and Firebase teams to unify full-stack developer experiences and publish standard developer recipes.
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class=&amp;quot;my-6&amp;quot;&amp;gt;
  &amp;lt;img src={goldenGateImg.src} alt=&amp;quot;San Francisco Golden Gate Bridge&amp;quot; class=&amp;quot;w-full rounded-xl border border-[var(--border-main)] shadow-sm bg-[#13151a]&amp;quot; /&amp;gt;
  &amp;lt;p class=&amp;quot;text-xs text-center text-[var(--fg-secondary)] mt-2&amp;quot;&amp;gt;Living and engineering in the San Francisco Bay Area&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;{/* Chapter 5 */}
&amp;lt;div class=&amp;quot;relative pl-6&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;absolute -left-[27px] top-1.5 w-3 h-3 rounded-full bg-red-500 border-4 border-[#13151a]&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;span class=&amp;quot;text-xs font-bold uppercase tracking-wider text-red-500&amp;quot;&amp;gt;Present&amp;lt;/span&amp;gt;
&amp;lt;h3 class=&amp;quot;text-lg font-bold text-[var(--fg-primary)] mt-1 mb-3&amp;quot;&amp;gt;Chapter 5: Open Source &amp;amp; Sound Synthesis&amp;lt;/h3&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-4&amp;quot;&amp;gt;
Beyond my work at Google, open-source development remains a core focus. I ported Preact signals to Dart (&lt;strong&gt;Signals&lt;/strong&gt;), authored structured PowerPoint compilers in Dart (&lt;strong&gt;Flutter PPTX&lt;/strong&gt;), engineered distributed synchronization modules for SQLite (&lt;strong&gt;sqlite-crdt&lt;/strong&gt;), and have shipped over 40 plugins to the pub.dev directory.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-4&amp;quot;&amp;gt;
In my personal life, I am happily engaged to the love of my life, Molly ❤️. I compose original ambient music, build web-synthesizers, and teach music production classes with MIDI hardware interfaces.
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;flex flex-col gap-6 my-6 max-w-2xl mx-auto&amp;quot;&amp;gt;
  &amp;lt;img src={familyImg.src} alt=&amp;quot;Rody Davis Family&amp;quot; class=&amp;quot;w-full rounded-xl border border-[var(--border-main)] shadow-sm bg-[#13151a]&amp;quot; /&amp;gt;
  &amp;lt;img src={rodyMollyImg.src} alt=&amp;quot;Rody and Molly&amp;quot; class=&amp;quot;w-full rounded-xl border border-[var(--border-main)] shadow-sm bg-[#13151a]&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;p class=&amp;quot;text-xs text-center text-[var(--fg-secondary)] mt-2&amp;quot;&amp;gt;Cherished moments with family and Molly ❤️&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;🛠️ Technical Competency Matrix&lt;/h2&gt;
&lt;p&gt;&amp;lt;table width=&amp;quot;100%&amp;quot; text-align=&amp;quot;left&amp;quot; class=&amp;quot;text-sm text-[var(--fg-secondary)]&amp;quot;&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th width=&amp;quot;50%&amp;quot;&amp;gt;💻 Core Technologies&amp;lt;/th&amp;gt;
&amp;lt;th width=&amp;quot;50%&amp;quot;&amp;gt;🧱 Frameworks &amp;amp; Tools&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Languages:&amp;lt;/strong&amp;gt; JavaScript, TypeScript, Dart, Go, Node.js, Kotlin, Swift, C, C#, SQL&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Open Source:&amp;lt;/strong&amp;gt; Dart Signals, Flutter PPTX, sqlite-crdt&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;AI Tools:&amp;lt;/strong&amp;gt; Gemini API, Gemma&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td valign=&amp;quot;top&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Frameworks:&amp;lt;/strong&amp;gt; Flutter, Angular, Lit, Web Components, Material Design&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Development Tools:&amp;lt;/strong&amp;gt; Figma, VS Code, Git, Android Studio, SQLite, Docker&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Concepts:&amp;lt;/strong&amp;gt; On-device AI, Mobile Dev, Offline-First Architecture, CI/CD&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;📣 Public Speaking &amp;amp; Presentations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google I/O &amp;amp; Cloud Next:&lt;/strong&gt; Delivered keynotes and technical talks on serverless deployments, generative UI, and full-stack app architecture.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevShow:&lt;/strong&gt; Recorded 15+ episodes for Google&apos;s longest-running video developer show.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internal Classes:&lt;/strong&gt; Taught music production and MIDI hardware classes across Google engineering teams.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Home</title><link>https://rodydavis.com/welcome/</link><guid isPermaLink="true">https://rodydavis.com/welcome/</guid><description>Rody Davis is a Senior Developer Advocate for Antigravity, building Web Components and Dart/Flutter packages, and is also a musician and podcast host.</description><pubDate>Wed, 20 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;div class=&amp;quot;mb-8&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;/welcome-banner.svg&amp;quot; alt=&amp;quot;Rody Davis - Creative Engineering &amp;amp; Advocacy&amp;quot; class=&amp;quot;w-full rounded-xl border border-[var(--border-main)] shadow-sm bg-[#13151a] block&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&amp;quot;text-lg sm:text-xl leading-relaxed text-[var(--fg-primary)] mt-6 mb-8 font-normal&amp;quot;&amp;gt;
Supporting developer success through key platform launches, synthesizing user feedback, creating comprehensive code examples and documentation, and empowering the community through talks, technical videos, and direct advocacy.
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Glassmorphic Roles Grid --&amp;gt;
&amp;lt;div class=&amp;quot;grid grid-cols-1 md:grid-cols-3 gap-6 mb-12&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;bg-[var(--bg-sidebar)] border border-[var(--border-main)] rounded-xl p-6 transition-all duration-300 hover:border-[var(--header-h1)] shadow-sm&amp;quot;&amp;gt;
&amp;lt;h3 class=&amp;quot;mt-0 text-[var(--header-h1)] flex items-center gap-2 text-lg font-bold&amp;quot;&amp;gt;
📣 Developer Advocacy
&amp;lt;/h3&amp;gt;
&amp;lt;p class=&amp;quot;mb-0 text-sm text-[var(--fg-secondary)] leading-relaxed&amp;quot;&amp;gt;
Synthesizing user feedback, building robust SDK integrations, and delivering clear documentation, blogs, and talks that empower developers worldwide.
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bg-[var(--bg-sidebar)] border border-[var(--border-main)] rounded-xl p-6 transition-all duration-300 hover:border-[var(--header-h1)] shadow-sm&amp;quot;&amp;gt;
&amp;lt;h3 class=&amp;quot;mt-0 text-[var(--header-h1)] flex items-center gap-2 text-lg font-bold&amp;quot;&amp;gt;
🧱 UI Tooling &amp;amp; Architecture
&amp;lt;/h3&amp;gt;
&amp;lt;p class=&amp;quot;mb-0 text-sm text-[var(--fg-secondary)] leading-relaxed&amp;quot;&amp;gt;
Creating visual builders, rich component libraries, and modular widget kits to streamline visual design-to-code pipelines.
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bg-[var(--bg-sidebar)] border border-[var(--border-main)] rounded-xl p-6 transition-all duration-300 hover:border-[var(--header-h1)] shadow-sm&amp;quot;&amp;gt;
&amp;lt;h3 class=&amp;quot;mt-0 text-[var(--header-h1)] flex items-center gap-2 text-lg font-bold&amp;quot;&amp;gt;
🎵 Creative &amp;amp; Sound Synthesis
&amp;lt;/h3&amp;gt;
&amp;lt;p class=&amp;quot;mb-0 text-sm text-[var(--fg-secondary)] leading-relaxed&amp;quot;&amp;gt;
Exploring web synthesizers, digital signal processing (DSP), Ableton custom extensions, and podcast hosting for &amp;quot;Creative Engineering&amp;quot;.
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Featured Apps&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&amp;quot;grid grid-cols-1 md:grid-cols-3 gap-6 my-8&amp;quot;&amp;gt;
&amp;lt;!-- Widget Studio --&amp;gt;
&amp;lt;div class=&amp;quot;relative group rounded-xl bg-[var(--bg-sidebar)] border border-[var(--border-main)] hover:border-purple-500/50 p-6 flex flex-col justify-between transition-all duration-300 shadow-sm overflow-hidden&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;absolute top-0 right-0 w-24 h-24 bg-purple-500/5 rounded-bl-full pointer-events-none transition-all group-hover:bg-purple-500/10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;div class=&amp;quot;flex items-center gap-3 mb-3&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;https://widget.studio/icons/Icon-192.png&amp;quot; alt=&amp;quot;Widget Studio Logo&amp;quot; class=&amp;quot;w-10 h-10 rounded-lg object-contain shadow-sm border border-purple-500/20 bg-purple-950/10&amp;quot; /&amp;gt;
&amp;lt;h3 class=&amp;quot;text-lg font-bold text-[var(--fg-primary)] m-0&amp;quot;&amp;gt;Widget Studio&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-6&amp;quot;&amp;gt;
A premium visual editor and widget building suite designed to compile component code in real time for modern web frameworks and Flutter.
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;a href=&amp;quot;https://widget.studio/&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot; class=&amp;quot;block w-full text-center px-4 py-2.5 rounded-lg bg-purple-600 hover:bg-purple-500 text-white text-xs font-semibold no-underline transition-colors duration-200&amp;quot; style=&amp;quot;color: #ffffff !important; text-decoration: none !important;&amp;quot;&amp;gt;
Launch Visual Editor
&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- The Pitch Pipe --&amp;gt;
&amp;lt;div class=&amp;quot;relative group rounded-xl bg-[var(--bg-sidebar)] border border-[var(--border-main)] hover:border-sky-500/50 p-6 flex flex-col justify-between transition-all duration-300 shadow-sm overflow-hidden&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;absolute top-0 right-0 w-24 h-24 bg-sky-500/5 rounded-bl-full pointer-events-none transition-all group-hover:bg-sky-500/10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;div class=&amp;quot;flex items-center gap-3 mb-3&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;https://pitchpipe.app/icons/Icon-192.png&amp;quot; alt=&amp;quot;The Pitch Pipe Logo&amp;quot; class=&amp;quot;w-10 h-10 rounded-lg object-contain shadow-sm border border-sky-500/20 bg-sky-950/10&amp;quot; /&amp;gt;
&amp;lt;h3 class=&amp;quot;text-lg font-bold text-[var(--fg-primary)] m-0&amp;quot;&amp;gt;The Pitch Pipe&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-6&amp;quot;&amp;gt;
An interactive ear training and vocal guide tool helping musicians perfect their tuning, scale recognition, and acoustic intervals.
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;a href=&amp;quot;https://pitchpipe.app/&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot; class=&amp;quot;block w-full text-center px-4 py-2.5 rounded-lg bg-sky-600 hover:bg-sky-500 text-white text-xs font-semibold no-underline transition-colors duration-200&amp;quot; style=&amp;quot;color: #ffffff !important; text-decoration: none !important;&amp;quot;&amp;gt;
Open Vocal Guide
&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- The Pocket Piano --&amp;gt;
&amp;lt;div class=&amp;quot;relative group rounded-xl bg-[var(--bg-sidebar)] border border-[var(--border-main)] hover:border-emerald-500/50 p-6 flex flex-col justify-between transition-all duration-300 shadow-sm overflow-hidden&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;absolute top-0 right-0 w-24 h-24 bg-emerald-500/5 rounded-bl-full pointer-events-none transition-all group-hover:bg-emerald-500/10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;div class=&amp;quot;flex items-center gap-3 mb-3&amp;quot;&amp;gt;
&amp;lt;img src=&amp;quot;https://pocketpiano.app/icons/Icon-192.png&amp;quot; alt=&amp;quot;The Pocket Piano Logo&amp;quot; class=&amp;quot;w-10 h-10 rounded-lg object-contain shadow-sm border border-emerald-500/20 bg-emerald-950/10&amp;quot; /&amp;gt;
&amp;lt;h3 class=&amp;quot;text-lg font-bold text-[var(--fg-primary)] m-0&amp;quot;&amp;gt;The Pocket Piano&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;p class=&amp;quot;text-sm text-[var(--fg-secondary)] leading-relaxed mb-6&amp;quot;&amp;gt;
A highly responsive virtual synthesizer and interactive keyboard that enables portable melody building right in your browser.
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;a href=&amp;quot;https://pocketpiano.app/&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot; class=&amp;quot;block w-full text-center px-4 py-2.5 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white text-xs font-semibold no-underline transition-colors duration-200&amp;quot; style=&amp;quot;color: #ffffff !important; text-decoration: none !important;&amp;quot;&amp;gt;
Play Synthesizer
&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Current Work&lt;/h2&gt;
&lt;p&gt;Currently supporting &lt;strong&gt;&lt;a href=&quot;https://antigravity.google&quot;&gt;Google Antigravity&lt;/a&gt;&lt;/strong&gt; developer success:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🚀 Empowering global developer communities by sharing modern architecture practices, widget kits, and robust SDK integrations.&lt;/li&gt;
&lt;li&gt;📝 Synthesizing user feedback, building comprehensive example applications, and leading workshops/technical recording sessions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ecosystem &amp;amp; Technical Foundations&lt;/h3&gt;
&lt;p&gt;Supporting developers across core Google platforms and surrounding tech stacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://flutter.dev&quot;&gt;Flutter &amp;amp; Dart&lt;/a&gt;&lt;/strong&gt;: Architecting highly responsive, multi-platform design-to-code packages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://ai.google.dev&quot;&gt;Gemini &amp;amp; Gemma (AI Logic)&lt;/a&gt;&lt;/strong&gt;: Writing production-ready generative workflows, function-calling models, and web UI integrations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://firebase.google.com&quot;&gt;Firebase&lt;/a&gt;&lt;/strong&gt;: Implementing unified authentication, remote configurations, and Firestore security systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Focus Areas &amp;amp; Creations&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&amp;quot;grid grid-cols-1 md:grid-cols-2 gap-8 my-6&amp;quot;&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;h3 class=&amp;quot;text-base font-bold text-[var(--fg-primary)] mb-3 flex items-center gap-2 m-0&amp;quot;&amp;gt;
&amp;lt;span class=&amp;quot;text-sky-500&amp;quot;&amp;gt;🧪&amp;lt;/span&amp;gt; Core Explorations
&amp;lt;/h3&amp;gt;
&amp;lt;ul class=&amp;quot;space-y-2 text-sm text-[var(--fg-secondary)] list-disc pl-4 mt-2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;quot;https://www.sqlite.org&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;SQLite Engine&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;: Building embedded database integrations.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;quot;https://flutter.dev&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;Dart/Flutter&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;: Creating robust package ecosystems.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;quot;https://aframe.io&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;VR/AR Experiences&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;: Experimenting with WebXR frameworks.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;quot;https://www.ableton.com&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;Ableton Logic&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;: Custom midi scripts &amp;amp; synthesizer integration.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;h3 class=&amp;quot;text-base font-bold text-[var(--fg-primary)] mb-3 flex items-center gap-2 m-0&amp;quot;&amp;gt;
&amp;lt;span class=&amp;quot;text-purple-500&amp;quot;&amp;gt;🎧&amp;lt;/span&amp;gt; Contributions &amp;amp; Media
&amp;lt;/h3&amp;gt;
&amp;lt;ul class=&amp;quot;space-y-2 text-sm text-[var(--fg-secondary)] list-disc pl-4 mt-2&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;Web Components&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;: Crafting lightweight, framework-agnostic elements.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;quot;https://pub.dev/publishers/rodydavis.com/packages&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;pub.dev Packages&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;: Publishing utility kits for the Flutter ecosystem.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;quot;https://podcasts.apple.com/us/podcast/creative-engineering/id1507852833&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;Podcast Host&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;: Producing episode scripts and hosting &amp;quot;Creative Engineering&amp;quot;.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;quot;https://soundcloud.com/theonlysounddr&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;Original Music&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;: Compositing soundscapes available on SoundCloud &amp;amp; Spotify.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Previous Projects&lt;/h2&gt;
&lt;p&gt;Deep engineering contribution history across key developer platforms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/rodydavis/firebase-studio&quot;&gt;Firebase Studio&lt;/a&gt; &amp;amp; CLI Tools&lt;/strong&gt;: Built initial user-focused UI frameworks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://idx.dev&quot;&gt;Project IDX&lt;/a&gt;&lt;/strong&gt;: Assisted early validation and SDK embedding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developers.google.com/solutions&quot;&gt;Developer Solutions&lt;/a&gt;&lt;/strong&gt;: Advocated and mapped client developer requirements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://lit.dev&quot;&gt;Lit Framework&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://material.io&quot;&gt;Material Design&lt;/a&gt;&lt;/strong&gt;: Standardized custom theme variables and templates.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>How to Deploy PocketBase to Cloud Run</title><link>https://rodydavis.com/backend/pocketbase-cloudrun/</link><guid isPermaLink="true">https://rodydavis.com/backend/pocketbase-cloudrun/</guid><description>Deploy PocketBase to Google Cloud Run using a step-by-step guide that leverages volume mounting to connect it with Google Cloud Storage.</description><pubDate>Sat, 18 Jan 2025 22:12:22 GMT</pubDate><content:encoded>&lt;h1&gt;How to Deploy PocketBase to Cloud Run&lt;/h1&gt;
&lt;p&gt;It is now possible to run &lt;a href=&quot;https://pocketbase.io/&quot;&gt;PocketBase&lt;/a&gt; on Google &lt;a href=&quot;https://cloud.google.com/run?hl=en&quot;&gt;CloudRun&lt;/a&gt; because of the recent support for &lt;a href=&quot;https://cloud.google.com/run/docs/configuring/services/cloud-storage-volume-mounts&quot;&gt;mounting volumes&lt;/a&gt;. This is a guide on how to deploy PocketBase on Google Cloud Run.&lt;/p&gt;
&lt;h2&gt;Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Scale to zero&lt;/li&gt;
&lt;li&gt;Infinite storage (and file deletion protection, file versions, and multi region)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pb_data&lt;/code&gt;/&lt;code&gt;pb_public&lt;/code&gt;/&lt;code&gt;pb_hooks&lt;/code&gt; all in the same file system&lt;/li&gt;
&lt;li&gt;Backups can be done either by PocketBase or by protecting the bucket&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Google Cloud project&lt;/li&gt;
&lt;li&gt;Google Cloud Storage bucket&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Fork &lt;a href=&quot;https://github.com/rodydavis/pocketbase-cloudrun/tree/main&quot;&gt;this repository&lt;/a&gt; or click &amp;quot;Use this template&amp;quot; to create your own repository.&lt;/p&gt;
&lt;h2&gt;Steps&lt;/h2&gt;
&lt;h3&gt;Create a new service&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/pb_cloud_run_1_3qclbfac0c.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Google Cloud Build&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Setup with Cloud Build
&lt;ul&gt;
&lt;li&gt;Repository Provider: &lt;code&gt;GitHub&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select Repository: &lt;code&gt;THIS_REPOSITORY_FORK&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Branch: &lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Build Configuration: &lt;code&gt;Dockerfile&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;General Settings&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Allow unauthenticated invocations&lt;/li&gt;
&lt;li&gt;CPU is only allocated when the service is handling requests&lt;/li&gt;
&lt;li&gt;Maximum number of requests per container is set to &lt;code&gt;1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Maximum number of containers is set to &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Timeout is set to &lt;code&gt;3600&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ingress is set to internal and &lt;code&gt;all&lt;/code&gt; traffic&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Container(s), Volumes, Networking, Security&lt;/h4&gt;
&lt;h5&gt;Volumes&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Add volume
&lt;ul&gt;
&lt;li&gt;Volume type: &lt;code&gt;Google Storage bucket&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Volume name: &lt;code&gt;remote-storage (or any name you want)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Bucket: &lt;code&gt;YOUR_BUCKET_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Read-only: &lt;code&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Container(s)&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Startup CPU boost is &lt;code&gt;enabled&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Volume mount (s)
&lt;ul&gt;
&lt;li&gt;Volume name: &lt;code&gt;remote-storage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Mount path: &lt;code&gt;/cloud/storage&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Add Health Checks&lt;/h4&gt;
&lt;p&gt;You can add a health check to your service that uses Pocketbase&apos;s health check endpoint &lt;code&gt;/api/health&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/pb_cloud_run_2_7911u5glhr.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Deploy and Wait&lt;/h3&gt;
&lt;p&gt;Now create the service and wait for the cloud build to finish.&lt;/p&gt;
&lt;p&gt;If everything goes well, you should see the service deployed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/pb_cloud_run_3_y6n1ukkd8u.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;What if I have local files that I want to use?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pb_data&lt;/code&gt;, &lt;code&gt;pb_public&lt;/code&gt;, and &lt;code&gt;pb_hooks&lt;/code&gt; are all directories you might use during development.&lt;/p&gt;
&lt;p&gt;You can upload these directories to your Google Cloud Storage bucket you created earlier to the root directory.&lt;/p&gt;
&lt;h3&gt;Can I use a custom domain?&lt;/h3&gt;
&lt;p&gt;Yes, you can use a custom domain. You can follow the guide on the &lt;a href=&quot;https://cloud.google.com/run/docs/mapping-custom-domains&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Migrating my Blog to PocketBase</title><link>https://rodydavis.com/blog/migrating-to-pocketbase/</link><guid isPermaLink="true">https://rodydavis.com/blog/migrating-to-pocketbase/</guid><description>The author details a multi-stage process of migrating their personal blog across various technologies—including Wix, Jekyll, 11ty, and web components—before moving to Obsidian.</description><pubDate>Mon, 20 Jan 2025 20:08:23 GMT</pubDate><content:encoded>&lt;h1&gt;Migrating my Blog to PocketBase&lt;/h1&gt;
&lt;p&gt;As the plight of every developer, we must constantly improve our personal website and blog to reflect our latest passion and exploration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/giphy_fa0f9ejfy5.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;...of course not all developers need to do this but it is a fun exercise none the less.&lt;/p&gt;
&lt;p&gt;I really enjoy being able to look at new tech stacks and find ways to simplify my setup or make it more fun to use.&lt;/p&gt;
&lt;h2&gt;Context&lt;/h2&gt;
&lt;p&gt;For those that have not been following my migrations, I have been an early adopter on various tech stacks and especially for my blog.&lt;/p&gt;
&lt;p&gt;My very first website for &lt;a href=&quot;https://%20rodydavis.com&quot;&gt;rodydavis.com&lt;/a&gt; was made with &lt;a href=&quot;https://wix.com/&quot;&gt;Wix&lt;/a&gt; which was great to get started but it was before I became a developer.&lt;/p&gt;
&lt;p&gt;I later migrated my blog to &lt;a href=&quot;https://pages.github.com/&quot;&gt;Github Pages&lt;/a&gt; with &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; and was very happy with it and introduced me to &lt;a href=&quot;https://dev.to/dailydevtips1/what-exactly-is-frontmatter-123g&quot;&gt;Frontmatter&lt;/a&gt; and &lt;a href=&quot;https://google.github.io/styleguide/docguide/style.html&quot;&gt;Markdown&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This worked for awhile, but then I wanted to make it a lot more customized which is when I migrated to &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;11ty&lt;/a&gt;. This was a dead simple migration as I just needed to move my blog posts and add some config for my website metadata.&lt;/p&gt;
&lt;p&gt;As much as I loved the customization, it became hard to make my blog depend on &lt;a href=&quot;https://nodejs.org/en&quot;&gt;Node&lt;/a&gt; and &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npm&lt;/a&gt; but I still adopted it and setup some &lt;a href=&quot;https://github.com/features/actions&quot;&gt;Github Actions&lt;/a&gt; to build my website when I push commits.&lt;/p&gt;
&lt;p&gt;I then migrated my blog to a custom solution based on &lt;a href=&quot;https://lit.dev/&quot;&gt;lit&lt;/a&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_components&quot;&gt;web components&lt;/a&gt; but did not last long as it was mostly just for exploration. In the past I even had a version of my website built with &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;The next big migration was to &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt; which allowed me to publish my website with &lt;a href=&quot;https://obsidian.md/publish&quot;&gt;Obsidian Publish&lt;/a&gt; very easily from my phone or laptop.&lt;/p&gt;
&lt;p&gt;This finally broke me free of needing to bundle my website and was very happy with this setup, which leads me to today...&lt;/p&gt;
&lt;h2&gt;PocketBase&lt;/h2&gt;
&lt;p&gt;For those that are not familiar, &lt;a href=&quot;https://pocketbase.io/&quot;&gt;PocketBase&lt;/a&gt; is a &lt;a href=&quot;https://go.dev/&quot;&gt;Go&lt;/a&gt; library that compiles to a single binary, backed by SQLite as an open source CMS.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/screenshot_2025_01_20_at_11_53_in1ca1lmdg.29AM.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This makes it dead simple to deploy to a small VPS and and just point your custom domain to it with free SSL cert generation using &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let&apos;s Encrypt&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Coolify&lt;/h2&gt;
&lt;p&gt;The other thing I am now doing is hosting my website on &lt;a href=&quot;https://www.coolify.io/&quot;&gt;Coolify&lt;/a&gt; via a &lt;a href=&quot;https://www.hetzner.com/&quot;&gt;Hetzner&lt;/a&gt; server.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/screenshot_2025_01_20_at_12_01_u1dsid5hmx.06PM.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This has been a joy to use and makes it easy to deploy to VPS you own in &lt;a href=&quot;https://cloud.google.com/&quot;&gt;GCP&lt;/a&gt;, &lt;a href=&quot;https://www.digitalocean.com/&quot;&gt;Digitial Ocean&lt;/a&gt;, &lt;a href=&quot;https://www.hetzner.com/&quot;&gt;Hetzner&lt;/a&gt; or even a &lt;a href=&quot;https://www.raspberrypi.com/&quot;&gt;Rasberry PI&lt;/a&gt; 👀.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now I can deploy my website with no build step (for the frontend) and have dynamic features like the &lt;a href=&quot;https://openheart.fyi/&quot;&gt;Open Heart Protocol&lt;/a&gt;, live view counts, dynamic tag pages, live editor on the web and generated RSS feed.&lt;/p&gt;
&lt;p&gt;If you want to see the &lt;strong&gt;source code&lt;/strong&gt; you can find it &lt;a href=&quot;https://github.com/rodydavis/rodydavis&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Privacy Policy</title><link>https://rodydavis.com/privacy/</link><guid isPermaLink="true">https://rodydavis.com/privacy/</guid><description>Rody Davis Productions&apos;s Privacy Policy outlines the collection, use, and disclosure of personal and non-personally identifying information gathered through its website, committing to protecting user privacy.</description><pubDate>Sat, 18 Jan 2025 19:15:26 GMT</pubDate><content:encoded>&lt;h1&gt;Privacy Policy&lt;/h1&gt;
&lt;p&gt;Your privacy is critically important to us.&lt;/p&gt;
&lt;p&gt;Rody Davis Productions is located at:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;Rody Davis Productions
6142 Pinewood Rd
Oakland CA, United States
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is Rody Davis Productions&apos;s policy to respect your privacy regarding any information we may collect while operating our website. This Privacy Policy applies to &lt;a href=&quot;https://rodydavis.com/&quot;&gt;https://rodydavis.com&lt;/a&gt; (hereinafter, &amp;quot;us&amp;quot;, &amp;quot;we&amp;quot;, or &amp;quot;&lt;a href=&quot;https://rodydavis.com%22/&quot;&gt;https://rodydavis.com&amp;quot;&lt;/a&gt;). We respect your privacy and are committed to protecting personally identifiable information you may provide us through the Website. We have adopted this privacy policy (&amp;quot;Privacy Policy&amp;quot;) to explain what information may be collected on our Website, how we use this information, and under what circumstances we may disclose the information to third parties. This Privacy Policy applies only to information we collect through the Website and does not apply to our collection of information from other sources.&lt;br&gt;
This Privacy Policy, together with the Terms and conditions posted on our Website, set forth the general rules and policies governing your use of our Website. Depending on your activities when visiting our Website, you may be required to agree to additional terms and conditions.&lt;/p&gt;
&lt;h2&gt;Website Visitors&lt;/h2&gt;
&lt;p&gt;Like most website operators, Rody Davis Productions collects non-personally-identifying information of the sort that web browsers and servers typically make available, such as the browser type, language preference, referring site, and the date and time of each visitor request. Rody Davis Productions&apos;s purpose in collecting non-personally identifying information is to better understand how Rody Davis Productions&apos;s visitors use its website. From time to time, Rody Davis Productions may release non-personally-identifying information in the aggregate, e.g., by publishing a report on trends in the usage of its website.&lt;br&gt;
Rody Davis Productions also collects potentially personally-identifying information like Internet Protocol (IP) addresses for logged in users and for users leaving comments on &lt;a href=&quot;https://rodydavis.com/&quot;&gt;https://rodydavis.com&lt;/a&gt; blog posts. Rody Davis Productions only discloses logged in user and commenter IP addresses under the same circumstances that it uses and discloses personally-identifying information as described below.&lt;/p&gt;
&lt;h2&gt;Gathering of Personally-Identifying Information&lt;/h2&gt;
&lt;p&gt;Certain visitors to Rody Davis Productions&apos;s websites choose to interact with Rody Davis Productions in ways that require Rody Davis Productions to gather personally-identifying information. The amount and type of information that Rody Davis Productions gathers depends on the nature of the interaction. For example, we ask visitors who sign up for a blog at &lt;a href=&quot;https://rodydavis.com/&quot;&gt;https://rodydavis.com&lt;/a&gt; to provide a username and email address.&lt;/p&gt;
&lt;h2&gt;Security&lt;/h2&gt;
&lt;p&gt;The security of your Personal Information is important to us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While we strive to use commercially acceptable means to protect your Personal Information, we cannot guarantee its absolute security.&lt;/p&gt;
&lt;h2&gt;Advertisements&lt;/h2&gt;
&lt;p&gt;Ads appearing on our website may be delivered to users by advertising partners, who may set cookies. These cookies allow the ad server to recognize your computer each time they send you an online advertisement to compile information about you or others who use your computer. This information allows ad networks to, among other things, deliver targeted advertisements that they believe will be of most interest to you. This Privacy Policy covers the use of cookies by Rody Davis Productions and does not cover the use of cookies by any advertisers.&lt;/p&gt;
&lt;h2&gt;Links To External Sites&lt;/h2&gt;
&lt;p&gt;Our Service may contain links to external sites that are not operated by us. If you click on a third party link, you will be directed to that third party&apos;s site. We strongly advise you to review the Privacy Policy and terms and conditions of every site you visit.&lt;br&gt;
We have no control over, and assume no responsibility for the content, privacy policies or practices of any third party sites, products or services.&lt;/p&gt;
&lt;h2&gt;Protection of Certain Personally-Identifying Information&lt;/h2&gt;
&lt;p&gt;Rody Davis Productions discloses potentially personally-identifying and personally-identifying information only to those of its employees, contractors and affiliated organizations that (i) need to know that information in order to process it on Rody Davis Productions&apos;s behalf or to provide services available at Rody Davis Productions&apos;s website, and (ii) that have agreed not to disclose it to others. Some of those employees, contractors and affiliated organizations may be located outside of your home country; by using Rody Davis Productions&apos;s website, you consent to the transfer of such information to them. Rody Davis Productions will not rent or sell potentially personally-identifying and personally-identifying information to anyone. Other than to its employees, contractors and affiliated organizations, as described above, Rody Davis Productions discloses potentially personally-identifying and personally-identifying information only in response to a subpoena, court order or other governmental request, or when Rody Davis Productions believes in good faith that disclosure is reasonably necessary to protect the property or rights of Rody Davis Productions, third parties or the public at large.&lt;br&gt;
If you are a registered user of &lt;a href=&quot;https://rodydavis.com/&quot;&gt;https://rodydavis.com&lt;/a&gt; and have supplied your email address, Rody Davis Productions may occasionally send you an email to tell you about new features, solicit your feedback, or just keep you up to date with what&apos;s going on with Rody Davis Productions and our products. We primarily use our blog to communicate this type of information, so we expect to keep this type of email to a minimum. If you send us a request (for example via a support email or via one of our feedback mechanisms), we reserve the right to publish it in order to help us clarify or respond to your request or to help us support other users. Rody Davis Productions takes all measures reasonably necessary to protect against the unauthorized access, use, alteration or destruction of potentially personally-identifying and personally-identifying information.&lt;/p&gt;
&lt;h2&gt;Aggregated Statistics&lt;/h2&gt;
&lt;p&gt;Rody Davis Productions may collect statistics about the behavior of visitors to its website. Rody Davis Productions may display this information publicly or provide it to others. However, Rody Davis Productions does not disclose your personally-identifying information.&lt;/p&gt;
&lt;h2&gt;Cookies&lt;/h2&gt;
&lt;p&gt;To enrich and perfect your online experience, Rody Davis Productions uses &amp;quot;Cookies&amp;quot;, similar technologies and services provided by others to display personalized content, appropriate advertising and store your preferences on your computer.&lt;br&gt;
A cookie is a string of information that a website stores on a visitor&apos;s computer, and that the visitor&apos;s browser provides to the website each time the visitor returns. Rody Davis Productions uses cookies to help Rody Davis Productions identify and track visitors, their usage of &lt;a href=&quot;https://rodydavis.com/&quot;&gt;https://rodydavis.com&lt;/a&gt;, and their website access preferences. Rody Davis Productions visitors who do not wish to have cookies placed on their computers should set their browsers to refuse cookies before using Rody Davis Productions&apos;s websites, with the drawback that certain features of Rody Davis Productions&apos;s websites may not function properly without the aid of cookies.&lt;br&gt;
By continuing to navigate our website without changing your cookie settings, you hereby acknowledge and agree to Rody Davis Productions&apos;s use of cookies.&lt;/p&gt;
&lt;p&gt;Privacy Policy Changes&lt;br&gt;
Although most changes are likely to be minor, Rody Davis Productions may change its Privacy Policy from time to time, and in Rody Davis Productions&apos;s sole discretion. Rody Davis Productions encourages visitors to frequently check this page for any changes to its Privacy Policy. Your continued use of this site after any change in this Privacy Policy will constitute your acceptance of such change.&lt;/p&gt;
&lt;h2&gt;Credit &amp;amp; Contact Information&lt;/h2&gt;
&lt;p&gt;This privacy policy was created at &lt;a href=&quot;https://termsandconditionstemplate.com/privacy-policy-generator/&quot;&gt;https://termsandconditionstemplate.com/privacy-policy-generator/&lt;/a&gt;. If you have any questions about this Privacy Policy, please contact us via or phone.&lt;/p&gt;
</content:encoded></item><item><title>How to do Offline Recommendations with SQLite and Gemini</title><link>https://rodydavis.com/ai/vector-recommendations/</link><guid isPermaLink="true">https://rodydavis.com/ai/vector-recommendations/</guid><description>Learn how to implement offline content recommendations by using Gemini to generate text embeddings and storing/querying them efficiently within a local SQLite database.</description><pubDate>Mon, 10 Feb 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;How to do Offline Recommendations with SQLite and Gemini&lt;/h1&gt;
&lt;p&gt;When working with a CMS (like &lt;a href=&quot;https://pocketbase.io&quot;&gt;PocketBase&lt;/a&gt;) it is common to add some sort of recommendatios for related content. For example you can have a list of blog posts, and show related posts either by random selection or recently viewed.&lt;/p&gt;
&lt;p&gt;I first learned about this technique from &lt;a href=&quot;https://aaronfrancis.com&quot;&gt;Aaron Francis&lt;/a&gt; on his YouTube channel:&lt;/p&gt;
&lt;h2&gt;Text Embeddings&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ai.google.dev/gemini-api/docs/embeddings&quot;&gt;Text embeddings&lt;/a&gt; are a way to convert a chunk of text into a an array of numbers. Having a mathematical representation means we can easily store them in a database and run common functions to calculate the distances between vectors that we have stored.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You will need an &lt;a href=&quot;https://aistudio.google.com/apikey&quot;&gt;API Key from AI Studio&lt;/a&gt; to generate the descriptions and embeddings.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In order to create the embedding we need to first generate chunk small enough to fit in the embedding window size. For example we can use an LLM like &lt;a href=&quot;https://ai.google.dev/gemini-api/docs/text-generation?lang=go&quot;&gt;Gemini to generate a description&lt;/a&gt; for a blog post and then vectorize the description which we can store in the database.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We only need to generate a new embedding and description when the content changes which limits the billing costs to the frequency of the content changes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Storing the Vectors&lt;/h2&gt;
&lt;p&gt;To store the text embeddings as vectors we can save them in a &lt;a href=&quot;https://www.sqlite.org&quot;&gt;SQLite&lt;/a&gt; database using a &lt;a href=&quot;https://www.sqlite.org/loadext.html&quot;&gt;runtime loadable extension&lt;/a&gt; called &lt;a href=&quot;https://github.com/asg017/sqlite-vec&quot;&gt;sqlite-vec&lt;/a&gt;. Here is an example from the readme on how to query the vectors directly in SQLite:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;.load ./vec0

create virtual table vec_examples using vec0(
  sample_embedding float[8]
);

-- vectors can be provided as JSON or in a compact binary format
insert into vec_examples(rowid, sample_embedding)
  values
    (1, &apos;[-0.200, 0.250, 0.341, -0.211, 0.645, 0.935, -0.316, -0.924]&apos;),
    (2, &apos;[0.443, -0.501, 0.355, -0.771, 0.707, -0.708, -0.185, 0.362]&apos;),
    (3, &apos;[0.716, -0.927, 0.134, 0.052, -0.669, 0.793, -0.634, -0.162]&apos;),
    (4, &apos;[-0.710, 0.330, 0.656, 0.041, -0.990, 0.726, 0.385, -0.958]&apos;);


-- KNN style query
select
  rowid,
  distance
from vec_examples
where sample_embedding match &apos;[0.890, 0.544, 0.825, 0.961, 0.358, 0.0196, 0.521, 0.175]&apos;
order by distance
limit 2;
/*
┌───────┬──────────────────┐
│ rowid │     distance     │
├───────┼──────────────────┤
│ 2     │ 2.38687372207642 │
│ 1     │ 2.38978505134583 │
└───────┴──────────────────┘
*/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can just take the vectors we created earlier and store them in a table which can update as content changes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  content TEXT NOT NULL,
  description TEXT.
  embeddings TEXT
);

CREATE VIRTUAL TABLE vec_posts USING vec0(
  id INTEGER PRIMARY KEY,
  embedding float[768]
);

-- Sync vectors
INSERT INTO vec_posts(id, embedding) SELECT id, embeddings FROM posts;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;We could also setup triggers to keep them up to date but in PocketBase I am using event hooks to keep the virtual table udpated.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Generate the Recommendation&lt;/h2&gt;
&lt;p&gt;Now to generate the recommendation offline we just need to use one of the blog posts to use as the input query to then use &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html&quot;&gt;k-nearest neighbor search (kNN)&lt;/a&gt; to get N number of related posts.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT
  vec_posts.id as id,
  vec_posts.embedding as embedding,
  posts.title as title,
  posts.description as description,
  posts.slug as slug
FROM vec_posts
INNER JOIN vec_posts.id = posts.id
WHERE embedding match ?
AND k = 6
ORDER BY distance;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We just need to provide the ? argument with the vector of the currently selected blog post, and then after we filter out the current blog post from the list then we have the N closest number of blog posts that are related in a vector database.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This makes it so no matter how many times a blog posts is visited no network calls are made for the recommendation which enables this to scale really well.&lt;/p&gt;
&lt;p&gt;To see this in action you can click around on the various blog posts I have on my site and see the generated descriptions and related posts at the end of each article.&lt;/p&gt;
</content:encoded></item><item><title>How To Host Your Podcast For Free On Github Pages</title><link>https://rodydavis.com/blog/podcast-github-pages/</link><guid isPermaLink="true">https://rodydavis.com/blog/podcast-github-pages/</guid><description>Learn how to host your podcast for free using GitHub Pages, leveraging GitHub&apos;s CDN and Actions for seamless deployment and updates.</description><pubDate>Mon, 20 Jan 2025 01:07:48 GMT</pubDate><content:encoded>&lt;h1&gt;How To Host Your Podcast For Free On Github Pages&lt;/h1&gt;
&lt;p&gt;Do you have a story to tell and want to share it with the world but do not know where to start? Are you a developer looking to start a tech podcast? Are you looking to save money for hosting? Well this article is for you!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; You can fork &lt;a href=&quot;https://github.com/rodydavis/podcast-player&quot;&gt;this repo&lt;/a&gt; and customize it for your podcast!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/g_1_wz30qhrbqz.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pages.github.com/&quot;&gt;Github Pages&lt;/a&gt; allows you to host any website for free on Github. You can host any kind of file or content and will be distributed with Github CDN around the world. You can even setup your Podcast to release new content with &lt;a href=&quot;https://github.com/features/actions&quot;&gt;Github Actions&lt;/a&gt; for each new episode.&lt;/p&gt;
&lt;h2&gt;Create New Repo&lt;/h2&gt;
&lt;p&gt;If you already familiar with Github you can skip this step.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://help.github.com/en/enterprise/2.14/user/articles/creating-a-new-repository&quot;&gt;How to create a new Github repository.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Login or Create a free account for &lt;a href=&quot;https://github.com/&quot;&gt;Github&lt;/a&gt; and follow the instructions for creating the repo.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/g_2_om6lao7zki.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Clone the Repo&lt;/h2&gt;
&lt;p&gt;Now that you have the repo created on Github, download the project using Github Desktop.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://help.github.com/en/desktop/contributing-to-projects/cloning-a-repository-from-github-to-github-desktop&quot;&gt;How to clone a repository from Github.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After it finishes downloading you can open up the project in your favorite text editor. In this example we will be using VSCode.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Download Visual Studio Code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Create the Website&lt;/h2&gt;
&lt;p&gt;You can create the website with whatever tech stack you wish but in this example we will be using Flutter.&lt;/p&gt;
&lt;p&gt;You can skip this step if you do not want an online player.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://flutter.dev/&quot;&gt;Learn about Flutter.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Open up the project if you haven’t already with VSCode and open the terminal and type the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter create player
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the process finishes then you can edit the application UI. Make sure you have Flutter installed.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://flutter.dev/docs/get-started/install&quot;&gt;How to install Flutter.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Edit the website files to the following:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rodydavis/podcast-player/tree/master/player&quot;&gt;Flutter podcast template.&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Setting up Github Actions&lt;/h2&gt;
&lt;p&gt;Create a new Github Action that will release the new episode pushed to the master branch. If you chose not to have a podcast player and just want to host the files then you can add the audio files and the rss feed directly to the &lt;code&gt;gh-pages&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch and the files will be hosted instantly. Regardless make sure you have a file call &lt;code&gt;.nojekyll&lt;/code&gt; so the web deployment will be much faster.&lt;/p&gt;
&lt;h2&gt;Custom Domain&lt;/h2&gt;
&lt;p&gt;If you want to have your podcast hosted with a custom domain you can easily do this with Github pages. Follow this guide to set up your custom domain:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://help.github.com/en/github/working-with-github-pages/configuring-a-custom-domain-for-your-github-pages-site&quot;&gt;Custom domain on Github Pages.&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Releasing new Content&lt;/h2&gt;
&lt;p&gt;When you have a new episode to release the steps are very simple. Make sure to export your audio file to mono and use mp3 format so it is smaller that 100mb otherwise you will need to set up Git LFS for the repo.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://git-lfs.github.com/&quot;&gt;Setup Git LFS for Github repository.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Put the new mp3 audio file in the “player/web/audio” folder. Now edit the RSS feed which is located at “player/web/feed.xml” and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;
&amp;lt;rss xmlns:googleplay=&amp;quot;http://www.google.com/schemas/play-podcasts/1.0&amp;quot; xmlns:itunes=&amp;quot;http://www.itunes.com/dtds/podcast-1.0.dtd&amp;quot; xmlns:atom=&amp;quot;http://www.w3.org/2005/Atom&amp;quot; xmlns:rawvoice=&amp;quot;http://www.rawvoice.com/rawvoiceRssModule/&amp;quot; xmlns:content=&amp;quot;http://purl.org/rss/1.0/modules/content/&amp;quot; version=&amp;quot;2.0&amp;quot;&amp;gt;
  &amp;lt;channel&amp;gt;
    &amp;lt;title&amp;gt;Creative Engineering&amp;lt;/title&amp;gt;
    &amp;lt;googleplay:author&amp;gt;Rody Davis, Norbert Kozsir&amp;lt;/googleplay:author&amp;gt;
    &amp;lt;rawvoice:rating&amp;gt;TV-G&amp;lt;/rawvoice:rating&amp;gt;
    &amp;lt;rawvoice:location&amp;gt;San Francisco, California&amp;lt;/rawvoice:location&amp;gt;
    &amp;lt;rawvoice:frequency&amp;gt;Weekly&amp;lt;/rawvoice:frequency&amp;gt;
    &amp;lt;author&amp;gt;Rody Davis, Norbert Kozsir&amp;lt;/author&amp;gt;
    &amp;lt;itunes:author&amp;gt;Rody Davis, Norbert Kozsir&amp;lt;/itunes:author&amp;gt;
    &amp;lt;itunes:email&amp;gt;rody.davis.jr@gmail.com&amp;lt;/itunes:email&amp;gt;
    &amp;lt;itunes:category text=&amp;quot;Technology&amp;quot; /&amp;gt;
    &amp;lt;image&amp;gt;
      &amp;lt;url&amp;gt;https://rodydavis.github.io/podcast-player/img/icon.webp&amp;lt;/url&amp;gt;
      &amp;lt;title&amp;gt;Creative Engineering&amp;lt;/title&amp;gt;
      &amp;lt;link&amp;gt;https://rodydavis.github.io/podcast-player/&amp;lt;/link&amp;gt;
    &amp;lt;/image&amp;gt;
    &amp;lt;itunes:owner&amp;gt;
      &amp;lt;itunes:name&amp;gt;Rody Davis&amp;lt;/itunes:name&amp;gt;
      &amp;lt;itunes:email&amp;gt;rody.davis.jr@gmail.com&amp;lt;/itunes:email&amp;gt;
    &amp;lt;/itunes:owner&amp;gt;
    &amp;lt;itunes:keywords&amp;gt;flutter,dart,github,vr,ar,web&amp;lt;/itunes:keywords&amp;gt;
    &amp;lt;copyright&amp;gt;Rody Davis Productions 2020&amp;lt;/copyright&amp;gt;
    &amp;lt;description&amp;gt;Exploring Flutter, VR, AR, Cross-Platform Projects&amp;lt;/description&amp;gt;
    &amp;lt;googleplay:image href=&amp;quot;https://rodydavis.github.io/podcast-player/img/icon.webp&amp;quot; /&amp;gt;
    &amp;lt;language&amp;gt;en-us&amp;lt;/language&amp;gt;
    &amp;lt;itunes:explicit&amp;gt;no&amp;lt;/itunes:explicit&amp;gt;
    &amp;lt;pubDate&amp;gt;Mon, 13 Apr 2020 13:00:00 PDT&amp;lt;/pubDate&amp;gt;
    &amp;lt;link&amp;gt;https://rodydavis.github.io/podcast-player/feed.xml&amp;lt;/link&amp;gt;
    &amp;lt;itunes:image href=&amp;quot;https://rodydavis.github.io/podcast-player/img/icon.webp&amp;quot; /&amp;gt;
    &amp;lt;item&amp;gt;
      &amp;lt;author&amp;gt;Rody Davis, Norbert Kozsir&amp;lt;/author&amp;gt;
      &amp;lt;itunes:author&amp;gt;Rody Davis, Norbert Kozsir&amp;lt;/itunes:author&amp;gt;
      &amp;lt;title&amp;gt;Flutter Desktop/Web and VR&amp;lt;/title&amp;gt;
      &amp;lt;pubDate&amp;gt;Mon, 13 Apr 2020 13:00:00 GMT&amp;lt;/pubDate&amp;gt;
      &amp;lt;enclosure url=&amp;quot;https://rodydavis.github.io/podcast-player/audio/01-create-eng.mp3&amp;quot; type=&amp;quot;audio/mpeg&amp;quot; length=&amp;quot;34216300&amp;quot; /&amp;gt;
      &amp;lt;itunes:duration&amp;gt;54:08&amp;lt;/itunes:duration&amp;gt;
      &amp;lt;guid isPermaLink=&amp;quot;false&amp;quot;&amp;gt;cepod01&amp;lt;/guid&amp;gt;
      &amp;lt;itunes:explicit&amp;gt;no&amp;lt;/itunes:explicit&amp;gt;
      &amp;lt;description&amp;gt;
Norbert Kozsir - @norbertkozsir

https://twitter.com/norbertkozsir

https://github.com/norbert515


Rody Davis - @rodydavis

https://twitter.com/rodydavis

https://github.com/rodydavis

https://youtube.com/rodydavis

https://rodydavis.com

Our podcast player: 

https://rodydavis.github.io/podcast-player/
        &amp;lt;/description&amp;gt;
    &amp;lt;/item&amp;gt;
  &amp;lt;/channel&amp;gt;
&amp;lt;/rss&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For every episode you just have to add a new item to the feed and change the info for the episode. I would suggest putting the new episodes at the bottom.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;item&amp;gt;        
    &amp;lt;author&amp;gt;COMMA\_SEPERATED\_LIST\_OF\_AUTHORS&amp;lt;/author&amp;gt;        
    &amp;lt;itunes:author&amp;gt;COMMA\_SEPERATED\_LIST\_OF\_AUTHORS&amp;lt;/itunes:author&amp;gt;      
    &amp;lt;title&amp;gt;PODCAST\_EPISODE\_TITLE&amp;lt;/title&amp;gt;        
    &amp;lt;pubDate&amp;gt;Mon, 13 Apr 2020 13:00:00 GMT&amp;lt;/pubDate&amp;gt;        
    &amp;lt;enclosure url=&amp;quot;LINK\_TO\_AUDIO\_FILE&amp;quot; type=&amp;quot;audio/mpeg&amp;quot; length=&amp;quot;34216300&amp;quot; /&amp;gt;        
    &amp;lt;itunes:duration&amp;gt;54:08&amp;lt;/itunes:duration&amp;gt;        
    &amp;lt;guid isPermaLink=&amp;quot;false&amp;quot;&amp;gt;cepod01&amp;lt;/guid&amp;gt;      
    &amp;lt;itunes:explicit&amp;gt;no&amp;lt;/itunes:explicit&amp;gt;        
    &amp;lt;description&amp;gt;  
    Show Notes Here!         
    &amp;lt;/description&amp;gt;      
&amp;lt;/item&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/n_2_kvxta7kwbn.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Publishing&lt;/h2&gt;
&lt;p&gt;Once your Github Action is finished building you now have an RSS feed that you can use to submit to Apple Podcasts, Google Podcasts and Spotify for Podcasters.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;https://GITHUB\_USERNAME.github.io/GITHUB\_REPO/feed.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use this RSS Feed link to support any podcast player!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/g_3_v94djucpkr.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Hopefully you can see now how easy it is to host your podcast for free on Github Pages. You can find the final code for this example here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rodydavis/podcast-player&quot;&gt;Final Project.&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Live Example&lt;/h2&gt;
&lt;p&gt;My “Creative Engineering” podcast is hosted using this technique:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://podcasts.apple.com/us/podcast/creative-engineering/id1507852833&quot;&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://podcasts.google.com/feed/aHR0cHM6Ly9yb2R5ZGF2aXMuZ2l0aHViLmlvL2NyZWF0aXZlX2VuZ2luZWVyaW5nL2ZlZWQueG1s?ved=2ahUKEwiw5anO0dLqAhU2lZ4KHR3FDtcQ4aUDegQIARAC&amp;amp;hl=en-GB&quot;&gt;Google Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://open.spotify.com/show/3UTiK34aDOOSHFpGQ0RglN&quot;&gt;Spotify Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://music.amazon.com/podcasts/8884a5cb-a92a-4ba5-a3ef-906ac334386d/Creative-Engineering?ref=dm_wcp_pp_link_pr_s&quot;&gt;Amazon Music&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>How to do Bitwise operations in Dart</title><link>https://rodydavis.com/dart/bitwise/</link><guid isPermaLink="true">https://rodydavis.com/dart/bitwise/</guid><description>Dart supports bitwise operations (AND, OR, XOR, NAND, NOR) directly on `int` and `bool` types, demonstrated with working code examples.</description><pubDate>Sun, 19 Jan 2025 02:35:57 GMT</pubDate><content:encoded>&lt;h1&gt;How to do Bitwise operations in Dart&lt;/h1&gt;
&lt;p&gt;In Dart it is possible to do &lt;a href=&quot;https://en.wikipedia.org/wiki/Bitwise_operation#:~:text=In%20computer%20programming%2C%20a%20bitwise,directly%20supported%20by%20the%20processor.&quot;&gt;Bitwise Operations&lt;/a&gt; with &lt;strong&gt;int&lt;/strong&gt; and &lt;strong&gt;bool&lt;/strong&gt; types.&lt;/p&gt;
&lt;h2&gt;AND&lt;/h2&gt;
&lt;p&gt;Checks if the left and right side are both true. &lt;a href=&quot;https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=expressions-bitwise-operator&quot;&gt;Learn more&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;// int
print(0 &amp;amp; 1); // 0
print(1 &amp;amp; 0); // 0
print(1 &amp;amp; 1); // 1
print(0 &amp;amp; 0); // 0

// bool
print(false &amp;amp; true); // false
print(true &amp;amp; false); // false
print(true &amp;amp; true); // true
print(false &amp;amp; false); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;OR&lt;/h2&gt;
&lt;h3&gt;Inclusive&lt;/h3&gt;
&lt;p&gt;Checks if either the left or right side are true. &lt;a href=&quot;https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=be-logical-operator&quot;&gt;Learn more&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;// int
print(0 | 1); // 1
print(1 | 0); // 1
print(1 | 1); // 1
print(0 | 0); // 0

// bool
print(false | true); // true
print(true | false); // true
print(true | true); // true
print(false | false); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Exclusive&lt;/h3&gt;
&lt;p&gt;Checks if both the left or right side are true but not both. &lt;a href=&quot;https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=expressions-bitwise-exclusive-operator&quot;&gt;Learn more&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;// int
print(0 ^ 1); // 1
print(1 ^ 0); // 1
print(1 ^ 1); // 0
print(0 ^ 0); // 0

// bool
print(false ^ true); // true
print(true ^ false); // true
print(true ^ true); // false
print(false ^ false); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;NAND&lt;/h2&gt;
&lt;p&gt;Negated AND operation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;// int
print(~(0 &amp;amp; 1) &amp;amp; 1); // 1
print(~(1 &amp;amp; 0) &amp;amp; 1); // 1
print(~(1 &amp;amp; 1) &amp;amp; 1); // 0
print(~(0 &amp;amp; 0) &amp;amp; 1); // 1

// bool
print(!(false &amp;amp; true)); // true
print(!(true &amp;amp; false)); // true
print(!(true &amp;amp; true)); // false
print(!(false &amp;amp; false)); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;NOR&lt;/h2&gt;
&lt;p&gt;Negated inclusive OR operation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;// int
print(~(0 | 1) &amp;amp; 1); // 0
print(~(1 | 0) &amp;amp; 1); // 0
print(~(1 | 1) &amp;amp; 1); // 0
print(~(0 | 0) &amp;amp; 1); // 1

// bool
print(!(false | true)); // false
print(!(true | false)); // false
print(!(true | true)); // false
print(!(false | false)); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;XNOR&lt;/h2&gt;
&lt;p&gt;Negated exclusive OR operation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;// int
print(~(0 ^ 1) &amp;amp; 1); // 0
print(~(1 ^ 0) &amp;amp; 1); // 0
print(~(1 ^ 1) &amp;amp; 1); // 1
print(~(0 ^ 0) &amp;amp; 1); // 1

// bool
print(!(false ^ true)); // false
print(!(true ^ false)); // false
print(!(true ^ true)); // true
print(!(false ^ false)); // true
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>How to Run Astro SSR and PocketBase on the Same Server</title><link>https://rodydavis.com/astro/ssr-pocketbase/</link><guid isPermaLink="true">https://rodydavis.com/astro/ssr-pocketbase/</guid><description>Learn how to integrate and run Astro in Server-Side Rendering mode with PocketBase on the same server using a Go reverse proxy setup.</description><pubDate>Sat, 18 Jan 2025 22:05:53 GMT</pubDate><content:encoded>&lt;h1&gt;How to Run Astro SSR and PocketBase on the Same Server&lt;/h1&gt;
&lt;p&gt;In this article I will show you how to host &lt;a href=&quot;https://pocketbase.io/&quot;&gt;PocketBase&lt;/a&gt; and &lt;a href=&quot;https://docs.astro.build/en/guides/server-side-rendering/&quot;&gt;Astro in SSR&lt;/a&gt; mode on the same server. PocketBase does let you &lt;a href=&quot;https://pocketbase.io/docs/go-rendering-templates/&quot;&gt;render templates&lt;/a&gt; on the server but requires &lt;a href=&quot;https://pkg.go.dev/text/template&quot;&gt;Go Templates&lt;/a&gt; or pre-building with Static Site Generation (SSG).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This could also be modified to use your web server or framework of choice (&lt;a href=&quot;https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering&quot;&gt;Next.js&lt;/a&gt;, &lt;a href=&quot;https://kit.svelte.dev/docs/page-options&quot;&gt;SvelteKit&lt;/a&gt;, &lt;a href=&quot;https://qwik.builder.io/&quot;&gt;Qwik&lt;/a&gt;, &lt;a href=&quot;https://angular.io/guide/ssr&quot;&gt;Angular&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before getting started make sure you have the latest version of &lt;a href=&quot;https://nodejs.org/en/blog/announcements/v19-release-announce&quot;&gt;Node&lt;/a&gt; and &lt;a href=&quot;https://go.dev/doc/install&quot;&gt;Go&lt;/a&gt; installed locally.&lt;/p&gt;
&lt;h2&gt;Getting started&lt;/h2&gt;
&lt;p&gt;In a terminal run the following to create the base project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir pocketbase_astro_ssr
cd pocketbase_astro_ssr
mkdir server
mkdir www
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create the &lt;code&gt;server&lt;/code&gt; and &lt;code&gt;www&lt;/code&gt; folders in our project needed for both Astro and PocketBase.&lt;/p&gt;
&lt;h2&gt;Setting up the server&lt;/h2&gt;
&lt;p&gt;Create a file at &lt;code&gt;server/main.go&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;package main

import (
	&amp;quot;log&amp;quot;
	&amp;quot;net/http/httputil&amp;quot;
	&amp;quot;net/url&amp;quot;

	&amp;quot;github.com/labstack/echo/v5&amp;quot;
	&amp;quot;github.com/pocketbase/pocketbase&amp;quot;
	&amp;quot;github.com/pocketbase/pocketbase/core&amp;quot;
)

func main() {
    app := pocketbase.New()

    app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
        proxy := httputil.NewSingleHostReverseProxy(&amp;amp;url.URL{
			Scheme: &amp;quot;http&amp;quot;,
			Host:   &amp;quot;localhost:4321&amp;quot;,
		})
		e.Router.Any(&amp;quot;/*&amp;quot;, echo.WrapHandler(proxy))
		e.Router.Any(&amp;quot;/&amp;quot;, echo.WrapHandler(proxy))
        return nil
    })

    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are extending &lt;a href=&quot;https://pocketbase.io/docs/go-overview/&quot;&gt;PocketBase with Go&lt;/a&gt; and taking advantage of the &lt;a href=&quot;https://echo.labstack.com/docs/routing&quot;&gt;Echo router&lt;/a&gt; integration and using a &lt;a href=&quot;https://www.nginx.com/resources/glossary/reverse-proxy-server/#:~:text=A%20reverse%20proxy%20server%20is,traffic%20between%20clients%20and%20servers.&quot;&gt;reverse proxy&lt;/a&gt; to handle all requests not defined by PocketBase already and delegating them to Astro.&lt;/p&gt;
&lt;p&gt;Next run the following in a terminal to install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;go mod init server
go mod tidy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can start the server and move on to the client:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;go run main.go serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following and note that this will run in debug mode so all the SQL statements will start to show:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;2023/11/09 10:28:52 Server started at http://127.0.0.1:8090
├─ REST API: http://127.0.0.1:8090/api/
└─ Admin UI: http://127.0.0.1:8090/_/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Collections&lt;/h3&gt;
&lt;p&gt;Open up the Admin UI url and after creating a new admin user, create a new collection &lt;code&gt;items&lt;/code&gt; and add the following metadata:&lt;/p&gt;
&lt;p&gt;Column Name&lt;/p&gt;
&lt;p&gt;Column Type&lt;/p&gt;
&lt;p&gt;Column Settings&lt;/p&gt;
&lt;p&gt;title&lt;/p&gt;
&lt;p&gt;Plain Text&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/astro_ssr_1_l8se0qq5gx.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then update the API Rules to allow read access for list and view.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/astro_ssr_2_nbohwwy3lp.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is just for example purposes and on a production app you will rely on auth for ACLs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Create 3 new records with placeholder data.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/astro_ssr_3_s8uxkyvvla.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Creating the client&lt;/h2&gt;
&lt;p&gt;Now we can create the client that will be used to connect to PocketBase and serve all of the web traffic.&lt;/p&gt;
&lt;p&gt;Navigate to the &lt;code&gt;www&lt;/code&gt; directory and run the following in a terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm create astro@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Follow the prompts and enter the following:&lt;/p&gt;
&lt;p&gt;Question&lt;/p&gt;
&lt;p&gt;Answer&lt;/p&gt;
&lt;p&gt;Where should we create your new project?&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;How would you like to start your new project?&lt;/p&gt;
&lt;p&gt;Empty&lt;/p&gt;
&lt;p&gt;Install dependencies?&lt;/p&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;p&gt;Do you plan to write TypeScript?&lt;/p&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;p&gt;How strict should TypeScript be?&lt;/p&gt;
&lt;p&gt;Strict&lt;/p&gt;
&lt;p&gt;Initialize a new git repository?&lt;/p&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;p&gt;You can of course customize this as you need, but next we can install the dependencies needed by running the following in a terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm i -D @astrojs/node
npm i pocketbase
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next update &lt;code&gt;www/astro.config.mjs&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import { defineConfig } from &amp;quot;astro/config&amp;quot;;
import nodejs from &amp;quot;@astrojs/node&amp;quot;;

// https://astro.build/config
export default defineConfig({
  adapter: nodejs({
    mode: &amp;quot;standalone&amp;quot;,
  }),
  output: &amp;quot;server&amp;quot;,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will use Server Side Rendering (SSR) instead of Static Site Generation (SSG) when we run the web server.&lt;/p&gt;
&lt;h3&gt;UI&lt;/h3&gt;
&lt;h4&gt;Layouts&lt;/h4&gt;
&lt;p&gt;We can start by creating a shared layout for all the routes. Create a file at &lt;code&gt;www/src/layouts/Root.astro&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;---
interface Props {
  title: string;
}

const { title } = Astro.props;
---

&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;generator&amp;quot; content={Astro.generator} /&amp;gt;
    &amp;lt;title&amp;gt;{title}&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;slot /&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Routes&lt;/h4&gt;
&lt;p&gt;Now we can update the index &lt;code&gt;/&lt;/code&gt; route by updating the following file &lt;code&gt;www/src/pages/index.astro&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;---
import Root from &amp;quot;../layouts/Root.astro&amp;quot;;

import PocketBase from &amp;quot;pocketbase&amp;quot;;

const pb = new PocketBase(&amp;quot;http://127.0.0.1:8090&amp;quot;);
const items = pb.collection(&amp;quot;items&amp;quot;);
const records = await items.getFullList();
---

&amp;lt;Root title=&amp;quot;Items&amp;quot;&amp;gt;
  &amp;lt;h1&amp;gt;Items&amp;lt;/h1&amp;gt;
  &amp;lt;ul&amp;gt;
    {
      records.map((record) =&amp;gt; (
        &amp;lt;li&amp;gt;
          &amp;lt;a href={`/items/${record.id}`}&amp;gt;{record.title}&amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
      ))
    }
  &amp;lt;/ul&amp;gt;
&amp;lt;/Root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will call the &lt;code&gt;items&lt;/code&gt; collection on the server and render it with 0 JS on the client.&lt;/p&gt;
&lt;p&gt;Next create a file &lt;code&gt;www/src/pages/[...slug].astro&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;---
import Root from &amp;quot;../layouts/Root.astro&amp;quot;;

import PocketBase from &amp;quot;pocketbase&amp;quot;;

const slug = Astro.params.slug!;
const id = slug.split(&amp;quot;/&amp;quot;).pop()!;

const pb = new PocketBase(&amp;quot;http://127.0.0.1:8090&amp;quot;);
const items = pb.collection(&amp;quot;items&amp;quot;);

const records = await items.getList(1, 1, {
  filter: `id = &apos;${id}&apos;`,
});

if (records.items.length === 0) {
  return new Response(&amp;quot;Not found&amp;quot;, { status: 404 });
}

const {title} = records.items[0];
---

&amp;lt;Root {title}&amp;gt;
  &amp;lt;a href=&amp;quot;/&amp;quot;&amp;gt;Back&amp;lt;/a&amp;gt;
  &amp;lt;h1&amp;gt;{title}&amp;lt;/h1&amp;gt;
&amp;lt;/Root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is almost like before but now we can return a proper &lt;code&gt;404&lt;/code&gt; response if not found for an item.&lt;/p&gt;
&lt;h4&gt;Running&lt;/h4&gt;
&lt;p&gt;Now we can run the web server with the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&amp;gt; dev
&amp;gt; astro dev

  🚀  astro  v3.4.4 started in 67ms
  
  ┃ Local    http://localhost:4321/
  ┃ Network  use --host to expose
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then if we open up the PocketBase url &lt;code&gt;http://127.0.0.1:8090&lt;/code&gt; and you should see the following for the index route and detail routes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/astro_ssr_4_2jgteusxtt.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/astro_ssr_5_3u7bekhf36.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you can build a new binary for both the server and client and deploy them both on the same server instance. 🎉&lt;/p&gt;
&lt;p&gt;You can find the final code &lt;a href=&quot;https://github.com/rodydavis/pocketbase_astro_ssr&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>How to Build a WebRTC Signal Server with PocketBase</title><link>https://rodydavis.com/backend/pocketbase-webrtc/</link><guid isPermaLink="true">https://rodydavis.com/backend/pocketbase-webrtc/</guid><description>This guide details how to construct a WebRTC signal server using PocketBase, demonstrating real-time communication capabilities powered by SQLite and Server-Sent Events (SSE).</description><pubDate>Sat, 18 Jan 2025 22:32:05 GMT</pubDate><content:encoded>&lt;h1&gt;How to Build a WebRTC Signal Server with PocketBase&lt;/h1&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;If you are new to WebRTC then I suggest checking out this great Fireship video on &lt;a href=&quot;https://youtu.be/WmR9IMUD_CY?si=c6xEDVslDOsIJzyP&quot;&gt;WebRTC in 100 seconds&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Also if you are looking for a &lt;a href=&quot;https://firebase.google.com/&quot;&gt;Firebase&lt;/a&gt; example then check out &lt;a href=&quot;https://github.com/fireship-io/webrtc-firebase-demo&quot;&gt;this repository&lt;/a&gt; which this example is largely based on.&lt;/p&gt;
&lt;p&gt;This example is built using &lt;a href=&quot;https://pocketbase.io/&quot;&gt;PocketBase&lt;/a&gt; as the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling&quot;&gt;signal server&lt;/a&gt; for &lt;a href=&quot;https://webrtc.org/&quot;&gt;WebRTC&lt;/a&gt; and runs &lt;a href=&quot;https://www.sqlite.org/index.html&quot;&gt;SQLite&lt;/a&gt; on the server with easy to use realtime SDKs built on top of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events&quot;&gt;Server Sent Events (SSE)&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Setting up the server&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pocketbase.io/docs/&quot;&gt;Download PocketBase&lt;/a&gt; and create a new directory that we will use for the project.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir webrtc-pocketbase-demo
cd webrtc-pocketbase-demo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy the PocketBase binary into the directory you just created under a sub directory &lt;code&gt;.pb&lt;/code&gt;. If you are on MacOS you will need to &lt;a href=&quot;https://discussions.apple.com/thread/253681758&quot;&gt;allow the executable&lt;/a&gt; to run in settings.&lt;/p&gt;
&lt;p&gt;Start the PocketBase server with the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;.pb/pocketbase serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If all goes well you should see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;2023/11/04 15:10:56 Server started at http://127.0.0.1:8090
├─ REST API: http://127.0.0.1:8090/api/
└─ Admin UI: http://127.0.0.1:8090/_/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open up the Admin UI url and create a new username and password.&lt;/p&gt;
&lt;p&gt;For this example the email and password will be the following:&lt;/p&gt;
&lt;p&gt;Key&lt;/p&gt;
&lt;p&gt;Value&lt;/p&gt;
&lt;p&gt;Email&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:test@example.com&quot;&gt;test@example.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Password&lt;/p&gt;
&lt;p&gt;Test123456789&lt;/p&gt;
&lt;p&gt;You should now see the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_1_sxepcsqwya.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Creating the collections&lt;/h3&gt;
&lt;h4&gt;ice_servers&lt;/h4&gt;
&lt;p&gt;Create a new collection named &lt;code&gt;ice_servers&lt;/code&gt; with the following columns:&lt;/p&gt;
&lt;p&gt;Column Name&lt;/p&gt;
&lt;p&gt;Column Type&lt;/p&gt;
&lt;p&gt;url&lt;/p&gt;
&lt;p&gt;Plain text&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_2_18muflf746.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add the following API rule to the List/Search and View:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;@request.auth.id != &apos;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_3_zhpjwks4he.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;After the collection is created add 2 records for each of the following values for the url:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;stun:stun1.l.google.com:19302
stun:stun2.l.google.com:19302
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_4_u30xyj67qq.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;calls&lt;/h4&gt;
&lt;p&gt;Create a new collection named &lt;code&gt;calls&lt;/code&gt; with the following columns:&lt;/p&gt;
&lt;p&gt;Column Name&lt;/p&gt;
&lt;p&gt;Column Type&lt;/p&gt;
&lt;p&gt;Column Settings&lt;/p&gt;
&lt;p&gt;user_id&lt;/p&gt;
&lt;p&gt;Relation&lt;/p&gt;
&lt;p&gt;Non empty, &lt;code&gt;users&lt;/code&gt;, Cascade delete is &lt;code&gt;true&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;offer&lt;/p&gt;
&lt;p&gt;JSON&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;answer&lt;/p&gt;
&lt;p&gt;JSON&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_5_3x9zuluckq.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;it is also possible to limit the user to one call each by setting the Unique constraint on the &lt;code&gt;user_id&lt;/code&gt; column.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_6_epid0nfuym.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add the following API rule to all of the methods:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;@request.auth.id != &apos;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_7_6pyvjpk8jx.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;offer_candidates&lt;/h4&gt;
&lt;p&gt;Create a new collection named &lt;code&gt;offer_candidates&lt;/code&gt; with the following columns:&lt;/p&gt;
&lt;p&gt;Column Name&lt;/p&gt;
&lt;p&gt;Column Type&lt;/p&gt;
&lt;p&gt;Column Settings&lt;/p&gt;
&lt;p&gt;call_id&lt;/p&gt;
&lt;p&gt;Relation&lt;/p&gt;
&lt;p&gt;Non empty, &lt;code&gt;calls&lt;/code&gt;, Cascade delete is &lt;code&gt;true&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;data&lt;/p&gt;
&lt;p&gt;JSON&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_8_c1ecscf02n.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add the following API rule to all of the methods:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;@request.auth.id != &apos;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;answer_candidates&lt;/h4&gt;
&lt;p&gt;Create a new collection named &lt;code&gt;answer_candidates&lt;/code&gt; with the following columns:&lt;/p&gt;
&lt;p&gt;Column Name&lt;/p&gt;
&lt;p&gt;Column Type&lt;/p&gt;
&lt;p&gt;Column Settings&lt;/p&gt;
&lt;p&gt;call_id&lt;/p&gt;
&lt;p&gt;Relation&lt;/p&gt;
&lt;p&gt;Non empty, &lt;code&gt;calls&lt;/code&gt;, Cascade delete is &lt;code&gt;true&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;data&lt;/p&gt;
&lt;p&gt;JSON&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_9_8urvznju5m.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add the following API rule to all of the methods:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;@request.auth.id != &apos;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_10_rj34gi9mwc.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;users&lt;/h4&gt;
&lt;p&gt;For demo purposes we will not be including an auth form for the user, but to make the example simple create a new user with the same login info for the admin.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_11_a359eqpxdy.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_12_m6rwgbmoum.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Setting up the client&lt;/h2&gt;
&lt;p&gt;Navigate to the directory and run the following commands to get started:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init -y
npm i -D vite
npm i pocketbase
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;package.json&lt;/code&gt; to be the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &amp;quot;name&amp;quot;: &amp;quot;webrtc-pocketbase-demo&amp;quot;,
  &amp;quot;version&amp;quot;: &amp;quot;0.0.0&amp;quot;,
  &amp;quot;scripts&amp;quot;: {
    &amp;quot;dev&amp;quot;: &amp;quot;vite&amp;quot;,
    &amp;quot;build&amp;quot;: &amp;quot;vite build&amp;quot;,
    &amp;quot;serve&amp;quot;: &amp;quot;vite preview&amp;quot;
  },
  &amp;quot;devDependencies&amp;quot;: {
    &amp;quot;vite&amp;quot;: &amp;quot;^4.5.0&amp;quot;
  },
  &amp;quot;dependencies&amp;quot;: {
    &amp;quot;pocketbase&amp;quot;: &amp;quot;^0.19.0&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are in a Git repository update/create the &lt;code&gt;.gitignore&lt;/code&gt; to have the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;node_modules
.DS_Store
dist
dist-ssr
*.local
.pb
.env
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HTML&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;index.html&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;WebRTC Pocketbase Demo&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h2&amp;gt;1. Start your Webcam&amp;lt;/h2&amp;gt;
    &amp;lt;div class=&amp;quot;videos&amp;quot;&amp;gt;
      &amp;lt;span&amp;gt;
        &amp;lt;h3&amp;gt;Local Stream&amp;lt;/h3&amp;gt;
        &amp;lt;video id=&amp;quot;webcamVideo&amp;quot; autoplay playsinline&amp;gt;&amp;lt;/video&amp;gt;
      &amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt;
        &amp;lt;h3&amp;gt;Remote Stream&amp;lt;/h3&amp;gt;
        &amp;lt;video id=&amp;quot;remoteVideo&amp;quot; autoplay playsinline&amp;gt;&amp;lt;/video&amp;gt;
      &amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;button id=&amp;quot;webcamButton&amp;quot;&amp;gt;Start webcam&amp;lt;/button&amp;gt;
    &amp;lt;h2&amp;gt;2. Create a new Call&amp;lt;/h2&amp;gt;
    &amp;lt;button id=&amp;quot;callButton&amp;quot; disabled&amp;gt;Create Call (offer)&amp;lt;/button&amp;gt;``
    &amp;lt;h2&amp;gt;3. Join a Call&amp;lt;/h2&amp;gt;
    &amp;lt;p&amp;gt;Answer the call from a different browser window or device&amp;lt;/p&amp;gt;
    &amp;lt;input id=&amp;quot;callInput&amp;quot; /&amp;gt;
    &amp;lt;button id=&amp;quot;answerButton&amp;quot; disabled&amp;gt;Answer&amp;lt;/button&amp;gt;
    &amp;lt;h2&amp;gt;4. Hangup&amp;lt;/h2&amp;gt;
    &amp;lt;button id=&amp;quot;hangupButton&amp;quot; disabled&amp;gt;Hangup&amp;lt;/button&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/main.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;CSS&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;style.css&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;body {
  --text-color: #2c3e50;
  --video-background-color: #2c3e50;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto,
    Oxygen, Ubuntu, Cantarell, &amp;quot;Open Sans&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: var(--text-color);
  margin: 80px 10px;
}

video {
  width: 40vw;
  height: 30vw;
  margin: 2rem;
  background: var(--video-background-color);
}

.videos {
  display: flex;
  align-items: center;
  justify-content: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;JS&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;main.js&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import &amp;quot;./style.css&amp;quot;;

import PocketBase from &amp;quot;pocketbase&amp;quot;;

const pb = new PocketBase(&amp;quot;http://127.0.0.1:8090&amp;quot;);

const calls = pb.collection(&amp;quot;calls&amp;quot;);
const offerCandidates = pb.collection(&amp;quot;offer_candidates&amp;quot;);
const answerCandidates = pb.collection(&amp;quot;answer_candidates&amp;quot;);

const webcamButton = document.getElementById(&amp;quot;webcamButton&amp;quot;);
const webcamVideo = document.getElementById(&amp;quot;webcamVideo&amp;quot;);
const callButton = document.getElementById(&amp;quot;callButton&amp;quot;);
const callInput = document.getElementById(&amp;quot;callInput&amp;quot;);
const answerButton = document.getElementById(&amp;quot;answerButton&amp;quot;);
const remoteVideo = document.getElementById(&amp;quot;remoteVideo&amp;quot;);
const hangupButton = document.getElementById(&amp;quot;hangupButton&amp;quot;);

const auth = await pb
  .collection(&amp;quot;users&amp;quot;)
  .authWithPassword(
    import.meta.env.VITE_POCKETBASE_USERNAME,
    import.meta.env.VITE_POCKETBASE_PASSWORD
  );
const userId = auth.record.id;
const iceServers = await pb.collection(&amp;quot;ice_servers&amp;quot;).getFullList();

const servers = {
  iceServers: [{ urls: iceServers.map((e) =&amp;gt; e.url) }],
  iceCandidatePoolSize: 10,
};

const pc = new RTCPeerConnection(servers);
let localStream = null;
let remoteStream = null;

webcamButton.onclick = async () =&amp;gt; {
  localStream = await navigator.mediaDevices.getUserMedia({
    video: true,
    audio: true,
  });
  remoteStream = new MediaStream();

  localStream.getTracks().forEach((track) =&amp;gt; {
    pc.addTrack(track, localStream);
  });

  pc.ontrack = (event) =&amp;gt; {
    const stream = event.streams[0];
    stream.getTracks().forEach((track) =&amp;gt; {
      remoteStream.addTrack(track);
    });
  };

  webcamVideo.srcObject = localStream;
  remoteVideo.srcObject = remoteStream;

  callButton.disabled = false;
  answerButton.disabled = false;
  webcamButton.disabled = true;
};

callButton.onclick = async () =&amp;gt; {
  const call = await calls.create({
    user_id: userId,
  });
  const callId = call.id;

  callInput.value = callId;

  pc.onicecandidate = (event) =&amp;gt; {
    event.candidate &amp;amp;&amp;amp;
      offerCandidates.create({
        call_id: callId,
        data: event.candidate.toJSON(),
      });
  };

  const offerDescription = await pc.createOffer();
  await pc.setLocalDescription(offerDescription);

  const offer = {
    sdp: offerDescription.sdp,
    type: offerDescription.type,
  };

  await calls.update(callId, { offer });

  calls.subscribe(callId, (e) =&amp;gt; {
    const data = e.record;
    if (!pc.currentRemoteDescription &amp;amp;&amp;amp; data?.answer) {
      const answerDescription = new RTCSessionDescription(data.answer);
      pc.setRemoteDescription(answerDescription);
    }
  });

  answerCandidates.subscribe(&amp;quot;*&amp;quot;, (e) =&amp;gt; {
    if (e.action === &amp;quot;create&amp;quot;) {
      if (e.record?.call_id === callId) {
        const data = e.record.data;
        const candidate = new RTCIceCandidate(data);
        pc.addIceCandidate(candidate);
      }
    }
  });

  hangupButton.disabled = false;
};

answerButton.onclick = async () =&amp;gt; {
  const callId = callInput.value;
  const call = await calls.getOne(callId);

  pc.onicecandidate = (event) =&amp;gt; {
    event.candidate &amp;amp;&amp;amp;
      answerCandidates.create({
        call_id: call.id,
        data: event.candidate.toJSON(),
      });
  };

  const offerDescription = call.offer;
  const remoteDescription = new RTCSessionDescription(offerDescription);
  await pc.setRemoteDescription(remoteDescription);

  const answerDescription = await pc.createAnswer();
  await pc.setLocalDescription(answerDescription);

  const answer = {
    type: answerDescription.type,
    sdp: answerDescription.sdp,
  };

  await calls.update(call.id, { answer });

  offerCandidates.subscribe(&amp;quot;*&amp;quot;, async (e) =&amp;gt; {
    if (e.record?.call_id === call.id) {
      if (e.action === &amp;quot;create&amp;quot;) {
        const data = e.record.data;
        const candidate = new RTCIceCandidate(data);
        await pc.addIceCandidate(candidate);
      } else if (e.action === &amp;quot;delete&amp;quot;) {
        await offerCandidates.unsubscribe();
        window.location.reload();
      }
    }
  });
};

hangupButton.onclick = async () =&amp;gt; {
  const callId = callInput.value;
  pc.close();
  await calls.unsubscribe(callId);
  await calls.delete(callId);
  await answerCandidates.unsubscribe();
  window.location.reload();
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Running the example&lt;/h2&gt;
&lt;p&gt;Run the following command to start the client (make sure the server is running in a separate terminal client):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If successful you should see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;  VITE v4.5.0  ready in 547 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open up two browsers with the same url:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_13_9b6nwkhq3i.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the first window click &lt;code&gt;Start webcam&lt;/code&gt; and then &lt;code&gt;Create Call (offer)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This will ask for camera permission and then generate a new id and add it to the &lt;code&gt;Join a Call&lt;/code&gt; text field.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_14_rat2efjyy3.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Copy the new id and paste it in the second window field and click &lt;code&gt;Start webcam&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/web_rtc_15_438n00ql98.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then click &lt;code&gt;Hangup&lt;/code&gt; when you are done with the call 🎉.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can find the source code &lt;a href=&quot;https://github.com/rodydavis/webrtc-pocketbase-demo&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>How to Export SQLite Tables to CREATE Statements</title><link>https://rodydavis.com/dart/export-sqlite/</link><guid isPermaLink="true">https://rodydavis.com/dart/export-sqlite/</guid><description>A guide using Flutter and Dart to programmatically export all tables and indexes from a SQLite database into runnable SQL CREATE statements.</description><pubDate>Sat, 18 Jan 2025 20:43:35 GMT</pubDate><content:encoded>&lt;h1&gt;How to Export SQLite Tables to CREATE Statements&lt;/h1&gt;
&lt;p&gt;In this article I will show you how to export all the tables and indexes in a &lt;a href=&quot;https://www.sqlite.org/index.html&quot;&gt;SQLite&lt;/a&gt; database to CREATE statements at runtime.&lt;/p&gt;
&lt;h2&gt;Getting started&lt;/h2&gt;
&lt;p&gt;Start by creating a new directory and &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir sqlite_introspect
cd sqlite_introspect
flutter create .
flutter pub add sqlite3 mustache_template
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will add the &lt;code&gt;sqlite3&lt;/code&gt; package which uses FFI to call the native executable and mustache that we will use for templates later.&lt;/p&gt;
&lt;h2&gt;Creating the database&lt;/h2&gt;
&lt;p&gt;Creating the database can be done either in memory or based on a local file. For this example we will use in memory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final Database db = sqlite3.openInMemory();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don&apos;t forget to dispose of the database after use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;db.dispose();
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Defining the template&lt;/h2&gt;
&lt;p&gt;Since we will be using &lt;a href=&quot;https://mustache.github.io/&quot;&gt;Mustache&lt;/a&gt; we can define the variables that we will pass to the template as JSON.&lt;/p&gt;
&lt;p&gt;Create a &lt;code&gt;TableInfo&lt;/code&gt; class that will store the fields and indexes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;class TableInfo {
  final String name;
  final List&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt; fields;
  final List&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt; indexes;

  TableInfo({
    required this.name,
    required this.fields,
    required this.indexes,
  });

  Map&amp;lt;String, dynamic&amp;gt; toJson() {
    return {
      &apos;name&apos;: name,
      &apos;fields&apos;: [
        for (var i = 0; i &amp;lt; fields.length; i++)
          {
            &apos;index&apos;: i,
            &apos;table&apos;: name,
            &apos;isLast&apos;: i == fields.length - 1,
            ...fields[i],
          },
      ],
      &apos;indexes&apos;: [
        for (var i = 0; i &amp;lt; indexes.length; i++)
          {
            &apos;index&apos;: i,
            &apos;table&apos;: name,
            &apos;isLast&apos;: i == indexes.length - 1,
            ...indexes[i],
          },
      ],
    };
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can create the Mustache template used to build up the CREATE statements:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;const template = &apos;&apos;&apos;
{{#tables}}
CREATE TABLE {{name}} (
  {{#fields}}
  {{name}} {{#type}} {{.}}{{/type}}{{#notnull}} NOT NULL{{/notnull}}{{#pk}} PRIMARY KEY{{/pk}}{{#dflt_value}} DEFAULT {{.}}{{/dflt_value}}{{^isLast}},{{/isLast}}
  {{/fields}}
);
{{#indexes}}
CREATE {{#unique}} UNIQUE{{/unique}} {{name}}
ON {{table}}({{#values}} {{name}} {{/values}}{{^isLast}},{{/isLast}});
{{/indexes}}
{{/tables}}
&apos;&apos;&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exporting the PRAGMA&lt;/h2&gt;
&lt;p&gt;Now we can export the &lt;a href=&quot;https://www.sqlite.org/pragma.html&quot;&gt;PRAGMA&lt;/a&gt; for the database by exporting the list of tables, querying the column information and indexes about each one.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final tables = &amp;lt;TableInfo&amp;gt;[];
// Export table names
final tableNames = db
	.select(&amp;quot;SELECT name FROM sqlite_master WHERE type=&apos;table&apos;;&amp;quot;)
	.map((e) =&amp;gt; e[&apos;name&apos;] as String);
for (final t in tableNames) {
  // Export column information
  final info = db.select(&apos;PRAGMA table_info($t);&apos;);
  final tbl = TableInfo(name: t, fields: [], indexes: []);
  for (final c in info) {
    tbl.fields.add(c);
  }
  // Export index names
  final indexList = db.select(&apos;PRAGMA index_list($t);&apos;);
  for (final index in indexList) {
    final name = index[&apos;name&apos;] as String;
    // Export index information
    final infos = db.select(&apos;PRAGMA index_info($name);&apos;);
    final indexValue = {...index, &apos;values&apos;: infos};
    tbl.indexes.add(indexValue);
  }
  tables.add(tbl);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Rendering the template&lt;/h2&gt;
&lt;p&gt;Now take the tables we just exported and pass them to the mustache template to render:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final tml = Template(template);
final args = {&amp;quot;tables&amp;quot;: tables.map((e) =&amp;gt; e.toJson()).toList()};
final str = tml.renderString(args);
print(str);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will now print out all the tables and indexes as CREATE as valid SQL. 🎉&lt;/p&gt;
</content:encoded></item><item><title>Various Ways to Invoke Functions in Dart</title><link>https://rodydavis.com/dart/function-invoking/</link><guid isPermaLink="true">https://rodydavis.com/dart/function-invoking/</guid><description>Dart provides multiple ways to invoke functions, allowing mixed positional and named arguments, utilizing the `.call()` operator, or using the `Function.apply()` method.</description><pubDate>Sun, 19 Jan 2025 02:43:50 GMT</pubDate><content:encoded>&lt;h1&gt;Various Ways to Invoke Functions in Dart&lt;/h1&gt;
&lt;p&gt;There are multiple ways to call a &lt;a href=&quot;https://dart.dev/language/functions&quot;&gt;Function&lt;/a&gt; in Dart.&lt;/p&gt;
&lt;p&gt;The examples below will assume the following function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;void myFunction(int a, int b, {int? c, int? d}) {
  print((a, b, c, d));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But recently I learned that you can call a functions positional arguments in any order mixed with the named arguments. 🤯&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;myFunction(1, 2, c: 3, d: 4);
myFunction(1, c: 3, d: 4, 2);
myFunction(c: 3, d: 4, 1, 2);
myFunction(c: 3, 1, 2, d: 4);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition you can use the &lt;a href=&quot;https://dart.dev/language/callable-objects&quot;&gt;&lt;code&gt;.call&lt;/code&gt;&lt;/a&gt; operator to invoke the function if you have a reference to it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;myFunction.call(1, 2, c: 3, d: 4);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use &lt;a href=&quot;https://api.flutter.dev/flutter/dart-core/Function/apply.html&quot;&gt;&lt;code&gt;Function.apply&lt;/code&gt;&lt;/a&gt; to dynamically invoke a function with a reference but it should be noted that it will effect js dart complication size and performance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;Function.apply(myFunction, [1, 2], {#c: 3, #d: 4});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of these methods print the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;(1, 2, 3, 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
</content:encoded></item><item><title>How to create HTML Web Components with Dart</title><link>https://rodydavis.com/dart/html-web-components/</link><guid isPermaLink="true">https://rodydavis.com/dart/html-web-components/</guid><description>Learn how to create re-usable, framework-agnostic HTML Web Components using Dart, allowing developers to build modern web applications and progressively enhance existing sites.</description><pubDate>Fri, 14 Feb 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;How to create HTML Web Components with Dart&lt;/h1&gt;
&lt;p&gt;I am a long time &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_components&quot;&gt;Web Components&lt;/a&gt; fan (since helping DevRel &lt;a href=&quot;https://lit.dev/&quot;&gt;lit.dev&lt;/a&gt; and &lt;a href=&quot;https://github.com/material-components/material-web&quot;&gt;Material Web Components&lt;/a&gt;) and have also loved writing &lt;a href=&quot;https://dart.dev/&quot;&gt;Dart&lt;/a&gt; in both &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; applications and full stack apps.&lt;/p&gt;
&lt;p&gt;Despite being &lt;a href=&quot;https://arewebcomponentsathingyet.com/&quot;&gt;used at so many companies&lt;/a&gt;, Web Components have faced a lot of pushback from JavaScript developers that use frameworks to target the web. ☹️&lt;/p&gt;
&lt;p&gt;What you may not realize is that the web has a way to create new HTML tags that can be used in &lt;strong&gt;ANY&lt;/strong&gt; JS framework or place that returns HTML and you can progressively enchance applications. 🤩&lt;/p&gt;
&lt;p&gt;Since they are custom HTML tags, if you swap implementations, you do not need to update where it is used and you can ship components a separate files &lt;a href=&quot;https://world.hey.com/dhh/modern-web-apps-without-javascript-bundling-or-transpiling-a20f2755&quot;&gt;instead of one big bundle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dart &lt;a href=&quot;https://github.com/dart-archive/web-components&quot;&gt;used to support Web Components&lt;/a&gt; at one point and was even used by a precursor to Lit in a product call &lt;a href=&quot;https://github.com/polymer-dart&quot;&gt;Polymer&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Creating a Web Component in Javascript&lt;/h2&gt;
&lt;p&gt;To create a web component in Javascript you just need to extend HTML element and provide callbacks for when the component is mounted.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class HelloWorld extends HTMLElement {
  static observedAttributes = [&amp;quot;name&amp;quot;];

  constructor() {
    super();
  }

  update() {
    this.innerHTML = `Hello: ${this.getAttribute(&apos;name&apos;)}`;
  }

  connectedCallback() {
    console.log(&amp;quot;Custom element added to page.&amp;quot;);
    this. update();
  }

  disconnectedCallback() {
    console.log(&amp;quot;Custom element removed from page.&amp;quot;);
  }

  adoptedCallback() {
    console.log(&amp;quot;Custom element moved to new page.&amp;quot;);
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(`Attribute ${name} has changed.`);
    if (name === &apos;name&apos;) {
      this. update();
    }
  }
}

customElements.define(&amp;quot;hello-world&amp;quot;, HelloWorld);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then use it in HTML like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;hello-world name=&amp;quot;Rody&amp;quot;&amp;gt;&amp;lt;/hello-world&amp;gt;
    &amp;lt;script src=&amp;quot;./index.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works really well, and we don&apos;t even need a build step to create them!&lt;/p&gt;
&lt;h2&gt;Creating Web Components with Dart&lt;/h2&gt;
&lt;p&gt;To create them on the Dart side we need to use the &lt;a href=&quot;https://dart.dev/interop/js-interop/usage&quot;&gt;js_interop package&lt;/a&gt; and the new &lt;a href=&quot;https://pub.dev/packages/web&quot;&gt;web package&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We need to create a factory on the dart side that can create these JS classes without actually being able to create a class in the normal way (since JS and Dart classes are different).&lt;/p&gt;
&lt;p&gt;There is a great API &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct&quot;&gt;&lt;code&gt;Reflect.construct()&lt;/code&gt;&lt;/a&gt; which allows us to take a normal function and invoke it class a class constructor. JavaScript did not always support native classes and was only added with &lt;a href=&quot;https://www.w3schools.com/js/js_es6.asp&quot;&gt;ES6&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By using this built in API, we can create the classes with just pure Dart:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:js_interop&apos;;
import &apos;dart:js_interop_unsafe&apos;;

import &apos;package:web/web.dart&apos;;

class WebComponent&amp;lt;T extends HTMLElement&amp;gt; {
  late T element;
  final String extendsType = &apos;HTMLElement&apos;;

  void connectedCallback() {}

  void disconnectedCallback() {}

  void adoptedCallback() {}

  void attributeChangedCallback(
    String name,
    String? oldValue,
    String? newValue,
  ) {}

  Iterable&amp;lt;String&amp;gt; get observedAttributes =&amp;gt; [];

  bool get formAssociated =&amp;gt; false;

  ElementInternals? get internals =&amp;gt; element[&apos;_internals&apos;] as ElementInternals?;
  set internals(ElementInternals? value) {
    element[&apos;_internals&apos;] = value;
  }

  R getRoot&amp;lt;R extends JSObject&amp;gt;() {
    final hasShadow = element.shadowRoot != null;
    return (hasShadow ? element.shadowRoot! : element) as R;
  }

  static void define(String tag, WebComponent Function() create) {
    final obj = _factory(create);
    window.customElements.define(tag, obj);
  }
}

@JS(&apos;Reflect.construct&apos;)
external JSAny _reflectConstruct(
  JSObject target,
  JSAny args,
  JSFunction constructor,
);

final _instances = &amp;lt;HTMLElement, WebComponent&amp;gt;{};

JSFunction _factory(WebComponent Function() create) {
  final base = create();
  final elemProto = globalContext[base.extendsType] as JSObject;
  late JSAny obj;

  JSAny constructor() {
    final args = &amp;lt;String&amp;gt;[].jsify()!;
    final self = _reflectConstruct(elemProto, args, obj as JSFunction);
    final el = self as HTMLElement;
    _instances.putIfAbsent(el, () =&amp;gt; create()..element = el);
    return self;
  }

  obj = constructor.toJS;
  obj = obj as JSObject;

  final observedAttributes = base.observedAttributes;
  final formAssociated = base.formAssociated;

  obj[&apos;prototype&apos;] = elemProto[&apos;prototype&apos;];
  obj[&apos;observedAttributes&apos;] = observedAttributes.toList().jsify()!;
  obj[&apos;formAssociated&apos;] = formAssociated.jsify()!;

  final prototype = obj[&apos;prototype&apos;] as JSObject;
  prototype[&apos;connectedCallback&apos;] = (HTMLElement instance) {
    _instances[instance]?.connectedCallback();
  }.toJSCaptureThis;
  prototype[&apos;disconnectedCallback&apos;] = (HTMLElement instance) {
    _instances[instance]?.disconnectedCallback();
    _instances.remove(instance);
  }.toJSCaptureThis;
  prototype[&apos;adoptedCallback&apos;] = (HTMLElement instance) {
    _instances[instance]?.adoptedCallback();
  }.toJSCaptureThis;
  prototype[&apos;attributeChangedCallback&apos;] = (
    HTMLElement instance,
    String name,
    String? oldName,
    String? newName,
  ) {
    _instances[instance]?.attributeChangedCallback(name, oldName, newName);
  }.toJSCaptureThis;

  return obj as JSFunction;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This may seem like a lot to digest, but that is ok. It simply does some JS magic to upgrade functions to classes and provide the correct callbacks to create the web components.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want a package that does this for you, &lt;a href=&quot;https://pub.dev/packages/html_web_components&quot;&gt;html_web_components&lt;/a&gt; is on pub.dev.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To create a Web Component like we did before, we can just extend the class and define the component.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:html_web_components/html_web_components.dart&apos;;

class HelloWorld extends WebComponent {
  @override
  List&amp;lt;String&amp;gt; observedAttributes = [&apos;name&apos;];

  void update() {
    element.innerText = &amp;quot;Hello: ${element.getAttribute(&apos;name&apos;)}!&amp;quot;;
  }

  @override
  void connectedCallback() {
    super.connectedCallback();
    update();
  }

  @override
  void attributeChangedCallback(
    String name,
    String? oldValue,
    String? newValue,
  ) {
    super.attributeChangedCallback(name, oldValue, newValue);
    if (observedAttributes.contains(name)) {
      update();
    }
  }
}

void main() {
  WebComponent.define(&apos;hello-world&apos;, HelloWorld.new);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should look very similar (that is the goal) and makes it so easy to publish the compoents or build a full web application with it.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Web Components allow you to upgrade your client side interactivity while having the freedom to use server rendering to create the template files or just use a SPA on the frontend. You can take these components and use them in &lt;strong&gt;ANY&lt;/strong&gt; JS frameworks! 🤯&lt;/p&gt;
&lt;p&gt;I would highly suggest that you try it out for yourself before you write off Web Components. This is especially true for Flutter developers wanting an alternative to Flutter web (and even use with &lt;a href=&quot;https://pub.dev/packages/jaspr&quot;&gt;Jaspr&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;You can take advantage of Dart&apos;s great ecosystem of packages on &lt;a href=&quot;https://pub.dev/&quot;&gt;pub.dev&lt;/a&gt; and the ability to compile to WASM and JS. If you use a builder like &lt;a href=&quot;https://pub.dev/packages/peanut&quot;&gt;peanut&lt;/a&gt; it will even create the script that tries to load WASM and can fallback to JS for you 🔥&lt;/p&gt;
&lt;p&gt;If you want to see the code, you can &lt;a href=&quot;https://github.com/rodydavis/dart-web-components&quot;&gt;find it on GitHub&lt;/a&gt;. Reach out if you have any questions or want to show off something cool you built with them!&lt;/p&gt;
</content:encoded></item><item><title>How to Print Multiple Objects to the Console with print() in Dart</title><link>https://rodydavis.com/dart/print-multiple-objects/</link><guid isPermaLink="true">https://rodydavis.com/dart/print-multiple-objects/</guid><description>To print multiple objects to the console in Dart, use Records, which allow grouping multiple distinct values into a single `Object?` printable unit.</description><pubDate>Sun, 19 Jan 2025 02:39:50 GMT</pubDate><content:encoded>&lt;h1&gt;How to Print Multiple Objects to the Console with print() in Dart&lt;/h1&gt;
&lt;p&gt;If you are coming from JavaScript you may be used to printing multiple objects to the console with &lt;code&gt;console.log()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;console.log(&apos;a&apos;, 1, &apos;b&apos;, 2); // a 1 b 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Dart we can only print &lt;code&gt;Object?&lt;/code&gt; to the console with &lt;a href=&quot;https://api.dart.dev/stable/3.3.1/dart-core/print.html&quot;&gt;&lt;code&gt;print()&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;print(1); // 1
print(null); // null
print({&apos;a&apos;: 1, &apos;b&apos;: 2}); // {a: 1, b: 1}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But it is totally possible to print multiple objects too, we need to use &lt;a href=&quot;https://dart.dev/language/records&quot;&gt;Records&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final number = 1;
final str = &apos;Hello World&apos;;

print((number, str));

print((DateTime.now(), str));

print((DateTime.now(), count: number, description: str));

print((DateTime.now(), StackTrace.current));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Print the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;(1, Hello World)
(2024-03-06 15:48:26.514, Hello World)
(2024-03-06 15:48:26.514, count: 1, description: Hello World)
(2024-03-06 15:48:26.514, Error
    at get current [as current] (https://storage.googleapis.com/nnbd_artifacts/3.3.0/dart_sdk.js:139991:30)
    at Object.main$0 [as main] (&amp;lt;anonymous&amp;gt;:52:94)
    at Object.main$ [as main] (&amp;lt;anonymous&amp;gt;:44:10)
    at &amp;lt;anonymous&amp;gt;:89:26
    at Object.execCb (https://dartpad.dev/require.js:5:16727)
    at e.check (https://dartpad.dev/require.js:5:10499)
    at e.&amp;lt;anonymous&amp;gt; (https://dartpad.dev/require.js:5:12915)
    at https://dartpad.dev/require.js:5:1542
    at https://dartpad.dev/require.js:5:13376
    at each (https://dartpad.dev/require.js:5:1020)
    at e.emit (https://dartpad.dev/require.js:5:13344)
    at e.check (https://dartpad.dev/require.js:5:11058)
    at e.enable (https://dartpad.dev/require.js:5:13242)
    at e.init (https://dartpad.dev/require.js:5:9605)
    at a (https://dartpad.dev/require.js:5:8305)
    at Object.completeLoad (https://dartpad.dev/require.js:5:15962)
    at HTMLScriptElement.onScriptLoad (https://dartpad.dev/require.js:5:16882))

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
</content:encoded></item><item><title>Check if an Object is Truthy in Dart</title><link>https://rodydavis.com/dart/truthy/</link><guid isPermaLink="true">https://rodydavis.com/dart/truthy/</guid><description>An extension method can be used in Dart to simulate JavaScript-style truthy checking, allowing developers to determine if objects and values are logically true within conditional statements.</description><pubDate>Sun, 19 Jan 2025 02:25:47 GMT</pubDate><content:encoded>&lt;h1&gt;Check if an Object is Truthy in Dart&lt;/h1&gt;
&lt;p&gt;If you are coming from language like JavaScript you may be used to checking if an object is &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Truthy&quot;&gt;truthy&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;if (true)
if ({})
if ([])
if (42)
if (&amp;quot;0&amp;quot;)
if (&amp;quot;false&amp;quot;)
if (new Date())
if (-42)
if (12n)
if (3.14)
if (-3.14)
if (Infinity)
if (-Infinity)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Dart you need to explicitly check if an object is not null, true/false or determine if the value is true based on the type.&lt;/p&gt;
&lt;p&gt;It is possible however to use Dart extensions to add the truthy capability.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;extension on Object? {
  bool get isTruthy =&amp;gt; truthy(this);
}

bool truthy(Object? val) {
  if (val == null) return false;
  if (val is bool) return val;
  if (val is num &amp;amp;&amp;amp; val == 0) return false;
  if (val is String &amp;amp;&amp;amp; (val == &apos;false&apos; || val == &apos;&apos;)) return false;
  if (val is Iterable &amp;amp;&amp;amp; val.isEmpty) return false;
  if (val is Map &amp;amp;&amp;amp; val.isEmpty) return false;
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will now make it possible for any object to be evaluated as a truthy value in if statements or value assignments.&lt;/p&gt;
&lt;p&gt;Prints the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;(null, false)
(, false)
(false, false)
(true, true)
(0, false)
(1, true)
(false, false)
(true, true)
([], false)
([1, 2, 3], true)
({}, false)
({1, 2, 3}, true)
({a: 1, b: 2}, true)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
</content:encoded></item><item><title>Calling the PaLM 2 API with Dart and Flutter</title><link>https://rodydavis.com/dart/palm-2-api/</link><guid isPermaLink="true">https://rodydavis.com/dart/palm-2-api/</guid><description>Learn how to call the PaLM 2 API using Dart and Flutter, including setup steps, dependency management, and API key handling.</description><pubDate>Sun, 19 Jan 2025 07:17:50 GMT</pubDate><content:encoded>&lt;h1&gt;Calling the PaLM 2 API with Dart and Flutter&lt;/h1&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;h3&gt;Prompt Template&lt;/h3&gt;
&lt;p&gt;To call the API with a prompt we can start by creating a template:&lt;/p&gt;
&lt;p&gt;This post will talk about how to call the PaLM 2 API with pure Dart in Flutter.&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Make sure you have the &lt;a href=&quot;https://docs.flutter.dev/get-started/install&quot;&gt;Flutter SDK installed&lt;/a&gt; and an &lt;a href=&quot;https://developers.generativeai.google/tutorials/setup&quot;&gt;API key for the PaLM API&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In a terminal window create a new directory and Flutter project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir flutter_ai_example
cd flutter_ai_example
flutter create .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the following to add the required dependencies to you &lt;code&gt;pubspec.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter pub add flutter_dotenv mustache_template http
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;API Keys&lt;/h3&gt;
&lt;p&gt;Now create a &lt;code&gt;.env&lt;/code&gt; and the root of the project and add the &lt;a href=&quot;https://developers.generativeai.google/tutorials/setup&quot;&gt;API key&lt;/a&gt; you created earlier to the file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;GOOGLE_AI_API_KEY=[Your API Key Here]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/palm_1_q1r92hn4gb.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Update &lt;code&gt;lib/main.dart&lt;/code&gt; main method to be async and load the &lt;code&gt;.env&lt;/code&gt; file on launch:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;+ import &apos;package:flutter_dotenv/flutter_dotenv.dart&apos;;

- void main() {
+ Future&amp;lt;void&amp;gt; main() async {
+	await dotenv.load(fileName: &amp;quot;.env&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This example loads the API key into the bundle as plain text but for production apps you need to call the API from a server&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Firebase has an &lt;a href=&quot;https://extensions.dev/extensions/googlecloud/palm-secure-backend&quot;&gt;extension for securely calling&lt;/a&gt; the API from your mobile app. 🎉&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/palm_2_twuynn8nx3.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In addition you can help prevent abuse to your API by using &lt;a href=&quot;https://firebase.google.com/docs/app-check&quot;&gt;App Check&lt;/a&gt;. 🔐&lt;/p&gt;
&lt;p&gt;If you want to learn more check out &lt;a href=&quot;https://firebase.google.com/codelabs/appcheck-web#0&quot;&gt;this codelab&lt;/a&gt;. 👀&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/palm_3_mbtl09n8wz.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3&gt;API Wrapper&lt;/h3&gt;
&lt;p&gt;Create a new file called &lt;code&gt;lib/palm_api.dart&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:convert&apos;;

import &apos;package:flutter_dotenv/flutter_dotenv.dart&apos;;
import &apos;package:mustache_template/mustache_template.dart&apos;;
import &apos;package:http/http.dart&apos; as http;

class PalmApi {
  final String template;
  final String model;

  PalmApi(
    this.template, {
    this.model = &apos;text-bison-001&apos;,
    this.settings = const {
      &amp;quot;temperature&amp;quot;: 0.7,
      &amp;quot;top_k&amp;quot;: 40,
      &amp;quot;top_p&amp;quot;: 0.95,
      &amp;quot;candidate_count&amp;quot;: 1,
      &amp;quot;max_output_tokens&amp;quot;: 1024,
      &amp;quot;stop_sequences&amp;quot;: [],
      &amp;quot;safety_settings&amp;quot;: [
        {&amp;quot;category&amp;quot;: &amp;quot;HARM_CATEGORY_DEROGATORY&amp;quot;, &amp;quot;threshold&amp;quot;: 1},
        {&amp;quot;category&amp;quot;: &amp;quot;HARM_CATEGORY_TOXICITY&amp;quot;, &amp;quot;threshold&amp;quot;: 1},
        {&amp;quot;category&amp;quot;: &amp;quot;HARM_CATEGORY_VIOLENCE&amp;quot;, &amp;quot;threshold&amp;quot;: 2},
        {&amp;quot;category&amp;quot;: &amp;quot;HARM_CATEGORY_SEXUAL&amp;quot;, &amp;quot;threshold&amp;quot;: 2},
        {&amp;quot;category&amp;quot;: &amp;quot;HARM_CATEGORY_MEDICAL&amp;quot;, &amp;quot;threshold&amp;quot;: 2},
        {&amp;quot;category&amp;quot;: &amp;quot;HARM_CATEGORY_DANGEROUS&amp;quot;, &amp;quot;threshold&amp;quot;: 2}
      ]
    },
  });

  Uri _createUrl(String apiKey) {
    const domain = &apos;https://generativelanguage.googleapis.com&apos;;
    final path = &apos;v1beta3/models/$model:generateText&apos;;
    return Uri.parse(&apos;$domain/$path?key=$apiKey&apos;);
  }

  final Map&amp;lt;String, Object?&amp;gt; settings;

  Future&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; execute([Map&amp;lt;String, Object?&amp;gt; args = const {}]) async {
    // Load the API key from .env
    final apiKey = dotenv.env[&apos;GOOGLE_AI_API_KEY&apos;];
    if (apiKey == null) throw Exception(&apos;GOOGLE_AI_API_KEY Required in .env&apos;);

    // Create the API url to call the correct model
    final uri = _createUrl(apiKey);

    // Render the prompt with the tokens
    final text = Template(template).renderString(args);

    // Call the API with the model settings and prompt template
    final response = await http.post(
      uri,
      headers: {&apos;Content-Type&apos;: &apos;application/json&apos;},
      body: jsonEncode({
        &amp;quot;prompt&amp;quot;: {&amp;quot;text&amp;quot;: text},
        ...settings
      }),
    );

    // Check for a successful response and parse out the results
    if (response.statusCode == 200) {
      final body = response.body;
      final json = jsonDecode(body) as Map&amp;lt;String, dynamic&amp;gt;;
      final candidates = (json[&apos;candidates&apos;] as List&amp;lt;dynamic&amp;gt;)
          .map((e) =&amp;gt; e as Map&amp;lt;String, dynamic&amp;gt;)
          .toList();
      return candidates.map((e) =&amp;gt; e[&apos;output&apos;] as String).toList();
    }

    // Default to empty result set
    return [];
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are using &lt;a href=&quot;https://mustache.github.io/&quot;&gt;Mustache&lt;/a&gt; to add tokens to our prompt that we pass to the API. This will feel familiar if you are used to working with &lt;a href=&quot;https://developers.generativeai.google/products/makersuite&quot;&gt;MakerSuite&lt;/a&gt; and makes it easier to iterate on prompts and desired outputs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also copy the config from MakerSuite if you want to change the model or other settings.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;h3&gt;Prompt Template&lt;/h3&gt;
&lt;p&gt;To call the API with a prompt we can start by creating a template:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;const _template = &apos;&apos;&apos;
Generate a valid SVG that does not include animations based on the following UI description &amp;quot;{{description}}&amp;quot;.

Make sure that the content is centered in the middle of the svg.

The width will be {{width}}.
The height will be {{height}}.
The viewbox will be 0 0 {{width}} {{height}}.
&apos;&apos;&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are adding tokens for description, width, and height that we will pass in later. Notice that the template is a &lt;code&gt;const&lt;/code&gt; since it will reuse this string at compile time.&lt;/p&gt;
&lt;p&gt;Now we can create the API wrapper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final api = PalmApi(_template);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Passing Tokens&lt;/h3&gt;
&lt;p&gt;To call the API we can pass in the tokens we defined earlier:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final results = await api.execute({
  &apos;description&apos;: &apos;Red rectangle with blue circle&apos;,
  &apos;width&apos;: 300,
  &apos;height&apos;: 600,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the API can return different results we can check for just the SVG code and extract it out:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final res = results.first;
var result = res.trim();
final startIdx = result.indexOf(&apos;```&apos;);
final endIdx = result.lastIndexOf(&apos;```&apos;);
result = result.substring(startIdx + 3, endIdx).trim();
if (result.startsWith(&apos;svg&apos;)) {
  result = result.replaceFirst(&apos;svg&apos;, &apos;&apos;).trim();
}
if (result.startsWith(&apos;xml&apos;)) {
  result = result.replaceFirst(&apos;xml&apos;, &apos;&apos;).trim();
}
result = result.trim();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;result&lt;/code&gt; is not empty then we can work with it as an SVG string now 🎉&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter_svg/flutter_svg.dart&apos;;
...
SvgPicture(SvgStringLoader(result))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you still want to explore PaLM 2 in Flutter further check out this &lt;a href=&quot;https://codelabs.developers.google.com/haiku-generator#0&quot;&gt;codelab&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Automate Flutter App Releases</title><link>https://rodydavis.com/flutter/automation/</link><guid isPermaLink="true">https://rodydavis.com/flutter/automation/</guid><description>Automate Flutter app releases using a comprehensive bash script that handles building the APK and IPA, calculating project size, and deploying versions to beta testing tracks.</description><pubDate>Sun, 19 Jan 2025 06:09:34 GMT</pubDate><content:encoded>&lt;h1&gt;Automate Flutter App Releases&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; You can find the script &lt;a href=&quot;https://gist.github.com/rodydavis/774b36e32d7efa882cca8dd16da6e74c&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;#!/bin/bash

echo &amp;quot;App Release Automator by @rodydavis&amp;quot;

action=&amp;quot;$1&amp;quot;
red=`tput setaf 1`
green=`tput setaf 2`
reset=`tput sgr0`

if [ ${action} = &amp;quot;build&amp;quot; ]; then

    echo &amp;quot;${green}Generating built files.. ${reset}&amp;quot;
    flutter packages pub run build_runner clean
    flutter packages pub run build_runner build --delete-conflicting-outputs

    pub global activate pubspec_version
    git commit -a -m &amp;quot;Build $(pubver bump patch)&amp;quot;
    
    echo &amp;quot;${green}Building Project...${reset}&amp;quot;
    find . -name &amp;quot;*-e&amp;quot; -type f -delete
    flutter format .
    flutter clean

    echo &amp;quot;${green}Project Size: $(find . -name &amp;quot;*.dart&amp;quot; | xargs cat | wc -c)${reset}&amp;quot;

    echo &amp;quot;${green}Building APK...${reset}&amp;quot;
    flutter build apk

    echo &amp;quot;${green}Builing IPA..${reset}&amp;quot;
    cd ./ios &amp;amp;&amp;amp; pod install &amp;amp;&amp;amp; pod repo update &amp;amp;&amp;amp; cd ..
    flutter build ios

    git commit -a -m &amp;quot;Project Rebuilt&amp;quot;


elif [ ${action} = &amp;quot;beta&amp;quot; ]; then

    echo &amp;quot;${green}Generating built files..${reset}&amp;quot;
    flutter packages pub run build_runner clean
    flutter packages pub run build_runner build --delete-conflicting-outputs

    pub global activate pubspec_version
    git commit -a -m &amp;quot;Beta $(pubver bump patch)&amp;quot;
    
    echo &amp;quot;${green}Building Project...${reset}&amp;quot;
    find . -name &amp;quot;*-e&amp;quot; -type f -delete
    flutter format .
    flutter clean

    echo &amp;quot;${green}Project Size: $(find . -name &amp;quot;*.dart&amp;quot; | xargs cat | wc -c)${reset}&amp;quot;

    echo &amp;quot;${green}Building APK...${reset}&amp;quot;
    flutter build apk

    echo &amp;quot;${green}Sending Android to Beta...${reset}&amp;quot;
    cd ./android &amp;amp;&amp;amp; fastlane beta &amp;amp;&amp;amp; cd ..

    echo &amp;quot;${green}Builing IPA..${reset}&amp;quot;
    flutter build ios

    echo &amp;quot;${green}Sending iOS to Beta..${reset}&amp;quot;
    cd ./ios &amp;amp;&amp;amp; fastlane beta &amp;amp;&amp;amp; cd ..

    git commit -a -m &amp;quot;Sent to Beta&amp;quot;


elif [ ${action} = &amp;quot;release&amp;quot; ]; then

    echo &amp;quot;${green}Generating built files..${reset}&amp;quot;
    flutter packages pub run build_runner clean
    flutter packages pub run build_runner build --delete-conflicting-outputs

    pub global activate pubspec_version
    git commit -a -m &amp;quot;Production $(pubver bump minor)&amp;quot;

    echo &amp;quot;${green}Building Project...${reset}&amp;quot;
    find . -name &amp;quot;*-e&amp;quot; -type f -delete
    flutter format .
    flutter clean

    echo &amp;quot;${green}Project Size: $(find . -name &amp;quot;*.dart&amp;quot; | xargs cat | wc -c)${reset}&amp;quot;

    echo &amp;quot;${green}Building APK...${reset}&amp;quot;
    flutter build apk

    echo &amp;quot;${green}Sending Android to Production...${reset}&amp;quot;
    cd ./android &amp;amp;&amp;amp; fastlane release &amp;amp;&amp;amp; cd ..

    echo &amp;quot;${green}Builing IPA..${reset}&amp;quot;
    flutter build ios

    echo &amp;quot;${green}Sending iOS to Production...${reset}&amp;quot;
    cd ./ios &amp;amp;&amp;amp; fastlane release &amp;amp;&amp;amp; cd ..

    git commit -a -m &amp;quot;Sent to Production&amp;quot;

fi

echo &amp;quot;${green}Successfully completed${reset}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fastlane setup in each directory&lt;/li&gt;
&lt;li&gt;build_runner as a dependency&lt;/li&gt;
&lt;li&gt;Git Project in VCS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Steps to Run:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download this file and put it at the root level of your flutter project&lt;/li&gt;
&lt;li&gt;Open the terminal and navigate to your project location&lt;/li&gt;
&lt;li&gt;Enter this command: &lt;code&gt;chmod +x release.sh&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Usage&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For beta: &lt;code&gt;./release.sh beta&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;For production: &lt;code&gt;./release.sh release&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It will do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bump the version numbers if you are using the version in the &lt;code&gt;pubspec.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Release the apps with Fastlane&lt;/li&gt;
&lt;li&gt;Format all Dart Files&lt;/li&gt;
&lt;li&gt;Clean Project&lt;/li&gt;
&lt;li&gt;Rebuild classes&lt;/li&gt;
&lt;li&gt;Add commit message&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make your life easier and automate your builds to beta and production!&lt;/p&gt;
&lt;h2&gt;What you need&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://fastlane.tools/&quot;&gt;Fastlane&lt;/a&gt; setup in each directory&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pub.dartlang.org/packages/build_runner&quot;&gt;build_runner&lt;/a&gt; as a dependency&lt;/li&gt;
&lt;li&gt;Git Project in VCS&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Initial Setup&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Download &lt;a href=&quot;https://gist.github.com/rodydavis/774b36e32d7efa882cca8dd16da6e74c&quot;&gt;this file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Put it at the root level of your flutter project&lt;/li&gt;
&lt;li&gt;Open the terminal and navigate to your project location&lt;/li&gt;
&lt;li&gt;Enter this command: chmod +x release.sh&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Usage&lt;/h2&gt;
&lt;p&gt;Now you can call this script!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For beta: &lt;code&gt;./release.sh beta&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;For production: &lt;code&gt;./release.sh release&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Bump the version numbers if you are using the version in the pubspec.yaml&lt;/li&gt;
&lt;li&gt;Release the apps with Fastlane&lt;/li&gt;
&lt;li&gt;Format all Dart Files&lt;/li&gt;
&lt;li&gt;Clean Project&lt;/li&gt;
&lt;li&gt;Rebuild classes&lt;/li&gt;
&lt;li&gt;Add commit messages&lt;/li&gt;
&lt;li&gt;Updates Cocoa Pods&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Flutter Terminal Cheat Sheet</title><link>https://rodydavis.com/flutter/cheat-sheet/</link><guid isPermaLink="true">https://rodydavis.com/flutter/cheat-sheet/</guid><description>A comprehensive cheat sheet of advanced Flutter commands detailing procedures for web deployment, iOS dependency management, code generation, project cleanup, and debugging.</description><pubDate>Mon, 20 Jan 2025 00:13:39 GMT</pubDate><content:encoded>&lt;h1&gt;Flutter Terminal Cheat Sheet&lt;/h1&gt;
&lt;h2&gt;Run Flutter web with SKIA&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter run -d web --release --dart-define=FLUTTER_WEB_USE_SKIA=true
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Run Flutter web with Canvas Kit&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter run -d chrome --release --dart-define=FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT=true
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build your Flutter web app to Github Pages to the docs folder&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter build web &amp;amp;&amp;amp; rm -rf ./docs &amp;amp;&amp;amp; mkdir ./docs &amp;amp;&amp;amp; cp -a ./build/web/. ./docs/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Clean rebuild CocoaPods&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd ios &amp;amp;&amp;amp; pod deintegrate &amp;amp;&amp;amp; pod cache clean —all &amp;amp;&amp;amp; pod install &amp;amp;&amp;amp; cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Sometimes with firebase you need to run: &lt;code&gt;pod update Firebase&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Create Dart package with Example&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter create -t plugin . &amp;amp;&amp;amp; flutter create -i swift -a kotlin --androidx example
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Watch Build Files&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter packages pub run build_runner watch  -—delete-conflicting-outputs
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Generate Build Files&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter packages pub run build_runner build  -—delete-conflicting-outputs
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build Bug Report&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter run —bug-report
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flutter generate test coverage&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter test --coverage &amp;amp;&amp;amp; genhtml -o coverage coverage/lcov.info
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Rebuild Flutter Cache&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter pub pub cache repair
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Clean every flutter project&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;find . -name &amp;quot;pubspec.yaml&amp;quot; -exec $SHELL -c &apos;
    echo &amp;quot;Done. Cleaning all projects.&amp;quot;
    for i in &amp;quot;$@&amp;quot; ; do
        DIR=$(dirname &amp;quot;${i}&amp;quot;)
        echo &amp;quot;Cleaning ${DIR}...&amp;quot;
        (cd &amp;quot;$DIR&amp;quot; &amp;amp;&amp;amp; flutter clean &amp;gt;/dev/null 2&amp;gt;&amp;amp;1)
    done
    echo &amp;quot;DONE!&amp;quot;
&apos; {} +
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conditional Export/Import&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;export &apos;unsupported.dart&apos;
    if (dart.library.html) &apos;web.dart&apos;
    if (dart.library.io) &apos;mobile.dart&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Kill Dart Running&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;killall -9 dart
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flutter scripts&lt;/h2&gt;
&lt;p&gt;Add all the scripts to your &lt;code&gt;pubspec.yaml&lt;/code&gt; with &lt;a href=&quot;https://pub.dev/packages/flutter_scripts&quot;&gt;flutter_scripts&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Displaying HTML in Flutter</title><link>https://rodydavis.com/flutter/display-html/</link><guid isPermaLink="true">https://rodydavis.com/flutter/display-html/</guid><description>Learn how to display and interact with complex HTML and web content in Flutter using the robust `easy_web_view` package, with specific setup guides for web and mobile platforms.</description><pubDate>Sun, 19 Jan 2025 08:08:22 GMT</pubDate><content:encoded>&lt;h1&gt;Displaying HTML in Flutter&lt;/h1&gt;
&lt;p&gt;Sometimes you have content in HTML that needs to be displayed and interacted with in Flutter.&lt;/p&gt;
&lt;p&gt;Online Demo:&lt;a href=&quot;https://rodydavis.github.io/easy_web_view/#/&quot;&gt;https://rodydavis.github.io/easy_web_view/#/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/html_1_yaak9yba8y.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;For those impatient I created a package for you to get all the following functionally and more here: &lt;a href=&quot;https://pub.dev/packages/easy_web_view&quot;&gt;https://pub.dev/packages/easy_web_view&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Create a new flutter project named whatever you want.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you plan on showing HTML content on iOS/Android you will need to add the following to your pubspec.yaml&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;dependencies:
    webview_flutter: ^0.3.15+1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Web&lt;/h2&gt;
&lt;p&gt;Reference: &lt;a href=&quot;https://github.com/rodydavis/easy_web_view/blob/master/lib/src/web.dart&quot;&gt;/lib/src/web.dart&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To show html on Flutter web we need to use an HTMLElementView. This is a platform view that allows us to display native content.&lt;/p&gt;
&lt;p&gt;We first need to register the Element and add all the options we need. Here we are creating an iFrame element and setting the source based on if it is markdown, html or a url.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/html_2_uowumu74gx.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;To display valid HTML you can set the src field to the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;_src = &amp;quot;data:text/html;charset=utf-8,&amp;quot; + Uri.encodeComponent(&amp;quot;HTML_CONTENT_HERE&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;For the package you can also pass markdown to the src and it will convert it for you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After you call the setup method it is now time to display your new platform view:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/html_3_d2x4h481p0.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You need to use the same viewType string as you registered for “registerViewFactory” method earlier.&lt;/p&gt;
&lt;p&gt;Finally you need to wrap it in a container or sized box with an explicit width and height!&lt;/p&gt;
&lt;h2&gt;Mobile&lt;/h2&gt;
&lt;p&gt;Reference: &lt;a href=&quot;https://github.com/rodydavis/easy_web_view/blob/master/lib/src/mobile.dart&quot;&gt;https://github.com/rodydavis/easy_web_view/blob/master/lib/src/mobile.dart&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mobile setup should be easier. Let’s add a method for updating the url that we will pass to the web view.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/html_4_can4qsn59m.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create the controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;WebViewController _controller;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And when ever the src changes call this method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;_controller.loadUrl(_updateUrl(widget.src), headers: widget.headers);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally lets show the html in the widget tree:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/html_5_ue0rqxe1sr.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to see a complete example and advanced use case view the source here: &lt;a href=&quot;https://github.com/rodydavis/easy_web_view&quot;&gt;https://github.com/rodydavis/easy_web_view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And if you just want to have it all done for you use this package: &lt;a href=&quot;https://pub.dev/packages/easy_web_view&quot;&gt;https://pub.dev/packages/easy_web_view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feel free to make PRs if you have anything that could help make it better too (Or if you find bugs).&lt;/p&gt;
&lt;p&gt;When you show HTML this way you will find that you can interact, select text and work with it just like you would it it were a regular web page. If you are using the package you can also just pass embedded content or html elements too without needing a full html valid file (YouTube video for example).&lt;/p&gt;
</content:encoded></item><item><title>Using Fastlane in Flutter and CI</title><link>https://rodydavis.com/flutter/fastlane/</link><guid isPermaLink="true">https://rodydavis.com/flutter/fastlane/</guid><description>This guide outlines how to use Fastlane to automate the build process for Flutter applications, including steps for initializing Fastlane and configuring scripts for generating APK (Android) and IPA (iOS) files for both general and beta releases.</description><pubDate>Mon, 20 Jan 2025 02:50:39 GMT</pubDate><content:encoded>&lt;h1&gt;Using Fastlane in Flutter and CI&lt;/h1&gt;
&lt;p&gt;Prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Understand what &lt;a href=&quot;https://fastlane.tools/&quot;&gt;Fastlane&lt;/a&gt; is and how it works&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Project builds correctly following these &lt;a href=&quot;https://flutter.dev/docs/deployment/cd&quot;&gt;docs&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Android app setup in &lt;a href=&quot;https://developer.android.com/distribute/console&quot;&gt;Google Play Console&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS app setup in &lt;a href=&quot;https://appstoreconnect.apple.com/&quot;&gt;AppStore Connect&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://flutter.dev/docs/get-started/install&quot;&gt;Flutter is installed&lt;/a&gt; and your project is created&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Steps&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open your Flutter project&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: cd ios&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: fastlane init and follow the prompts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace the Fastfile contents with this:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;#!/bin/bash

echo &amp;quot;App Release Automator by @rodydavis&amp;quot;

action=&amp;quot;$1&amp;quot;
red=`tput setaf 1`
green=`tput setaf 2`
reset=`tput sgr0`

if [ ${action} = &amp;quot;build&amp;quot; ]; then

    echo &amp;quot;${green}Generating built files.. ${reset}&amp;quot;
    flutter packages pub run build_runner clean
    flutter packages pub run build_runner build --delete-conflicting-outputs

    pub global activate pubspec_version
    git commit -a -m &amp;quot;Build $(pubver bump patch)&amp;quot;
    
    echo &amp;quot;${green}Building Project...${reset}&amp;quot;
    find . -name &amp;quot;*-e&amp;quot; -type f -delete
    flutter format .
    flutter clean

    echo &amp;quot;${green}Project Size: $(find . -name &amp;quot;*.dart&amp;quot; | xargs cat | wc -c)${reset}&amp;quot;

    echo &amp;quot;${green}Building APK...${reset}&amp;quot;
    flutter build apk

    echo &amp;quot;${green}Builing IPA..${reset}&amp;quot;
    cd ./ios &amp;amp;&amp;amp; pod install &amp;amp;&amp;amp; pod repo update &amp;amp;&amp;amp; cd ..
    flutter build ios

    git commit -a -m &amp;quot;Project Rebuilt&amp;quot;


elif [ ${action} = &amp;quot;beta&amp;quot; ]; then

    echo &amp;quot;${green}Generating built files..${reset}&amp;quot;
    flutter packages pub run build_runner clean
    flutter packages pub run build_runner build --delete-conflicting-outputs

    pub global activate pubspec_version
    git commit -a -m &amp;quot;Beta $(pubver bump patch)&amp;quot;
    
    echo &amp;quot;${green}Building Project...${reset}&amp;quot;
    find . -name &amp;quot;*-e&amp;quot; -type f -delete
    flutter format .
    flutter clean

    echo &amp;quot;${green}Project Size: $(find . -name &amp;quot;*.dart&amp;quot; | xargs cat | wc -c)${reset}&amp;quot;

    echo &amp;quot;${green}Building APK...${reset}&amp;quot;
    flutter build apk

    echo &amp;quot;${green}Sending Android to Beta...${reset}&amp;quot;
    cd ./android &amp;amp;&amp;amp; fastlane beta &amp;amp;&amp;amp; cd ..

    echo &amp;quot;${green}Builing IPA..${reset}&amp;quot;
    flutter build ios

    echo &amp;quot;${green}Sending iOS to Beta..${reset}&amp;quot;
    cd ./ios &amp;amp;&amp;amp; fastlane beta &amp;amp;&amp;amp; cd ..

    git commit -a -m &amp;quot;Sent to Beta&amp;quot;


elif [ ${action} = &amp;quot;release&amp;quot; ]; then

    echo &amp;quot;${green}Generating built files..${reset}&amp;quot;
    flutter packages pub run build_runner clean
    flutter packages pub run build_runner build --delete-conflicting-outputs

    pub global activate pubspec_version
    git commit -a -m &amp;quot;Production $(pubver bump minor)&amp;quot;

    echo &amp;quot;${green}Building Project...${reset}&amp;quot;
    find . -name &amp;quot;*-e&amp;quot; -type f -delete
    flutter format .
    flutter clean

    echo &amp;quot;${green}Project Size: $(find . -name &amp;quot;*.dart&amp;quot; | xargs cat | wc -c)${reset}&amp;quot;

    echo &amp;quot;${green}Building APK...${reset}&amp;quot;
    flutter build apk

    echo &amp;quot;${green}Sending Android to Production...${reset}&amp;quot;
    cd ./android &amp;amp;&amp;amp; fastlane release &amp;amp;&amp;amp; cd ..

    echo &amp;quot;${green}Builing IPA..${reset}&amp;quot;
    flutter build ios

    echo &amp;quot;${green}Sending iOS to Production...${reset}&amp;quot;
    cd ./ios &amp;amp;&amp;amp; fastlane release &amp;amp;&amp;amp; cd ..

    git commit -a -m &amp;quot;Sent to Production&amp;quot;

fi

echo &amp;quot;${green}Successfully completed${reset}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;
&lt;p&gt;Run: cd .. &amp;amp;&amp;amp; cd android&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: fastlane init and follow the prompts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace the Fastfile contents with this:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane

default_platform(:android)

platform :android do
  desc &amp;quot;Prepare and archive app&amp;quot;
  lane :prepare  do |options|
    #bundle_install
    Dir.chdir &amp;quot;../..&amp;quot; do
      sh(&amp;quot;flutter&amp;quot;, &amp;quot;packages&amp;quot;, &amp;quot;get&amp;quot;)
      sh(&amp;quot;flutter&amp;quot;, &amp;quot;clean&amp;quot;)
      sh(&amp;quot;flutter&amp;quot;, &amp;quot;build&amp;quot;, &amp;quot;appbundle&amp;quot;, &amp;quot;--release&amp;quot;)
    end
  end
  
  desc &amp;quot;Push a new beta build to Google Play&amp;quot;
  lane :beta do
    prepare(release: false)
    upload_to_play_store(
      track: &apos;beta&apos;,
      aab: &amp;quot;../build/app/outputs/bundle/release/app.aab&amp;quot;
    )
    add_git_tag(
      grouping: &amp;quot;fastlane-builds&amp;quot;,
      prefix: &amp;quot;v&amp;quot;,
      build_number: android_get_version_code
    )
    push_to_git_remote
  end

  desc &amp;quot;Push a new release build to the Google Play&amp;quot;
  lane :release do
    prepare(release: true)
    upload_to_play_store(
      track: &apos;production&apos;,
      aab: &amp;quot;../build/app/outputs/bundle/release/app.aab&amp;quot;
    )
    add_git_tag(
      grouping: &amp;quot;release&amp;quot;,
      prefix: &amp;quot;v&amp;quot;,
      build_number: android_get_version_name
    )
    push_to_git_remote
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;8&quot;&gt;
&lt;li&gt;
&lt;p&gt;Run: fastlane add_plugin versioning_android and enter your password if needed&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: cd ..&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you are ready to launch your app to beta!&lt;/p&gt;
&lt;p&gt;For ios run: cd ios &amp;amp;&amp;amp; fastlane beta&lt;/p&gt;
&lt;p&gt;For android run: cd android &amp;amp;&amp;amp; fastlane beta&lt;/p&gt;
&lt;p&gt;Stay tuned for an article soon where we use these fastlane sub folders for automating the releases on &lt;a href=&quot;https://github.com/features/actions&quot;&gt;Github Actions&lt;/a&gt; CI&lt;/p&gt;
</content:encoded></item><item><title>Creating Your First Flutter Project</title><link>https://rodydavis.com/flutter/getting-started/</link><guid isPermaLink="true">https://rodydavis.com/flutter/getting-started/</guid><description>This guide provides a comprehensive overview and tutorial for creating a first Flutter project, detailing how to use Google&apos;s cross-platform UI toolkit to build applications for iOS, Android, Web, and other platforms.</description><pubDate>Sun, 19 Jan 2025 07:34:27 GMT</pubDate><content:encoded>&lt;h1&gt;Creating Your First Flutter Project&lt;/h1&gt;
&lt;p&gt;Flutter is a UI Toolkit from Google allowing you to create expressive and unique experiences unmatched on any platform. You can write your UI once and run it everywhere. Yes everywhere! Web, iOS, Android, Windows, Linux, MacOS, Raspberry PI and much more…&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/flutter_1_zxbnyzfn8p.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you prefer a video you can follow the YouTube series I am doing called “Flutter Take 5” where I explore topics that you encounter when building a Flutter application. I will also give you tips and tricks as I go through the series.&lt;/p&gt;
&lt;p&gt;Or this short:&lt;/p&gt;
&lt;h2&gt;What is Flutter&lt;/h2&gt;
&lt;p&gt;Flutter recently crossed React Native on Github and now has more than 2 million developers using Flutter to create applications. There are more than 50,000 apps on Google Play alone published with Flutter.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://flutter.dev/&quot;&gt;Learn about Flutter.&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Getting started is very easy once you get the SDK installed. After it is installed creating new applications, plugins and packages is lighting fast. Follow this guide to install Flutter:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://flutter.dev/docs/get-started/install&quot;&gt;How to install Flutter.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One nice thing about Flutter is that it is developed in the open as an open source project that anyone can contribute to. If there is something missing you can easily fork the repo and make a PR for the missing functionality.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/flutter_2_7906zvehq3.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Create the Project&lt;/h2&gt;
&lt;p&gt;Now that you have Flutter installed it is time to create your first (Of Many 😉) Flutter project! Open up your terminal and navigate to wherever you want the application folder to be created. Once you “cd” into the directory you can type the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter create my_awesome_project
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can replace “my_awesome_project” with whatever you want the project to be called. It is important to use snake_case as it is the valid syntax for project names in dart.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/flutter_3_ex6m9fvb6h.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Congratulations you just created your first project!&lt;/p&gt;
&lt;h2&gt;Open the Project&lt;/h2&gt;
&lt;p&gt;So you may be wondering what we just created so let us dive in to the details. You can open up you project in VSCode if you have it installed by typing the following into terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd my_awesome_project &amp;amp;&amp;amp; code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can open up the folder in your favorite IDE if you prefer. Two important files to notice are the pubspec.yaml and lib/main.dart&lt;/p&gt;
&lt;p&gt;Your UI and Logic is located at “lib/main.dart” and you should see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with &amp;quot;flutter run&amp;quot;. You&apos;ll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // &amp;quot;hot reload&amp;quot; (press &amp;quot;r&amp;quot; in the console where you ran &amp;quot;flutter run&amp;quot;,
        // or simply save your changes to &amp;quot;hot reload&amp;quot; in a Flutter IDE).
        // Notice that the counter didn&apos;t reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
        // This makes the visual density adapt to the platform that you run
        // the app on. For desktop platforms, the controls will be smaller and
        // closer together (more dense) than on mobile platforms.
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: &apos;Flutter Demo Home Page&apos;),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked &amp;quot;final&amp;quot;.

  final String title;

  @override
  _MyHomePageState createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke &amp;quot;debug painting&amp;quot; (press &amp;quot;p&amp;quot; in the console, choose the
          // &amp;quot;Toggle Debug Paint&amp;quot; action from the Flutter Inspector in Android
          // Studio, or the &amp;quot;Toggle Debug Paint&amp;quot; command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            Text(
              &apos;You have pushed the button this many times:&apos;,
            ),
            Text(
              &apos;$_counter&apos;,
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can define any dependencies and plugins needed for the application at “pubspec.yaml” and you should see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;name: example
description: A new Flutter project.

# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: &apos;none&apos; # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: &amp;quot;&amp;gt;=2.7.0 &amp;lt;3.0.0&amp;quot;

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.3

dev_dependencies:
  flutter_test:
    sdk: flutter

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.webp
  #   - images/a_dot_ham.webp

  # An image asset can refer to one or more resolution-specific &amp;quot;variants&amp;quot;, see
  # https://flutter.dev/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this &amp;quot;flutter&amp;quot; section. Each entry in this list should have a
  # &amp;quot;family&amp;quot; key with the font family name, and a &amp;quot;fonts&amp;quot; key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Running the Project&lt;/h2&gt;
&lt;p&gt;Running the application is very easy too. While there are buttons in all the IDEs you can also run your project from the command line for quick testing. You can also configure &lt;a href=&quot;https://flutter.dev/desktop&quot;&gt;Flutter for Desktop&lt;/a&gt; and no need to wait for an emulator to warm up. Open your project and enter the following into terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter run -d macos
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the “-d macos” as you can customize what device you want to run on. You should see the following in terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;Building macOS application...                                           
Syncing files to device macOS...                                   141ms

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h Repeat this help message.
d Detach (terminate &amp;quot;flutter run&amp;quot; but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
An Observatory debugger and profiler on macOS is available at: [http://127.0.0.1:58932/f1Mspofty_k=/](http://127.0.0.1:58932/f1Mspofty_k=/)
Application finished.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also run multiple devices at the same time. You can find more info on the &lt;a href=&quot;https://github.com/flutter/flutter/wiki/Multi-device-debugging-in-VS-Code&quot;&gt;Flutter Octopus here&lt;/a&gt;. If everything went well you should see the following application launch:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/flutter_4_sh7qzq9ggv.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It is a pretty basic application at this point but it is important to show how easy it is to change the state in the application. You can rebuild the UI just by calling “setState()”.&lt;/p&gt;
&lt;h2&gt;Testing the Project&lt;/h2&gt;
&lt;p&gt;Testing is one of the reasons I love Flutter so much and it is dead simple to run and write tests for the project. If you look at the file “test/widget_test.dart” you should see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.

import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter_test/flutter_test.dart&apos;;

import &apos;package:example/main.dart&apos;;

void main() {
  testWidgets(&apos;Counter increments smoke test&apos;, (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp());

    // Verify that our counter starts at 0.
    expect(find.text(&apos;0&apos;), findsOneWidget);
    expect(find.text(&apos;1&apos;), findsNothing);

    // Tap the &apos;+&apos; icon and trigger a frame.
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Verify that our counter has incremented.
    expect(find.text(&apos;0&apos;), findsNothing);
    expect(find.text(&apos;1&apos;), findsOneWidget);
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can run these tests very easily. Open your project and type the following into the terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter test
00:07 +1: All tests passed!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like that all your tests will run and you can catch any bugs you missed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/flutter_5_fr3u6tytdx.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can also generate code coverage for your applications easily by typing the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter test --coverage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate a new file at “coverage/lcov.info” and will read the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;SF:lib/main.dart
DA:3,0
DA:4,0
DA:9,1
DA:11,1
DA:13,1
DA:27,1
DA:29,1
DA:35,2
DA:48,1
DA:49,1
DA:55,1
DA:56,2
DA:62,2
DA:66,1
DA:74,1
DA:75,1
DA:78,3
DA:80,1
DA:83,1
DA:99,1
DA:100,1
DA:103,1
DA:104,2
DA:105,3
DA:110,1
DA:111,1
DA:113,1
LF:27
LH:25
end_of_record
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now easily create badges and graphs with the LCOV data. Here is a package that will make that easier:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pub.dev/packages/test_coverage&quot;&gt;test_coverage | Dart Package&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Flutter makes it possible to build applications very quickly that do not depend on web or mobile technologies. It can familiar to writing a game as you have to design all your own UI. You can find the final source code here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rodydavis/flutter_take_5/tree/master/01_your_first_project&quot;&gt;Final source code.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can also find the Flutter source code here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/flutter/flutter&quot;&gt;Flutter source code.&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>How to Manage Multiple Flutter Versions with Git Worktrees and ZSH</title><link>https://rodydavis.com/flutter/git-worktree-channels/</link><guid isPermaLink="true">https://rodydavis.com/flutter/git-worktree-channels/</guid><description>Manage multiple Flutter SDK versions by using Git worktrees to create isolated directories for different channels (stable, beta, master), and ZSH aliases for streamlined command-line access.</description><pubDate>Sun, 19 Jan 2025 02:32:18 GMT</pubDate><content:encoded>&lt;h1&gt;How to Manage Multiple Flutter Versions with Git Worktrees and ZSH&lt;/h1&gt;
&lt;p&gt;If you have been using &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; for any length of time then you probably have needed to use multiple flutter versions across multiple projects.&lt;/p&gt;
&lt;p&gt;In the past I used to use &lt;a href=&quot;https://fvm.app/&quot;&gt;FVM&lt;/a&gt; (Flutter Version Management) which is similar to &lt;a href=&quot;https://github.com/nvm-sh/nvm&quot;&gt;NVM&lt;/a&gt; (Node Version Manager) in the JS world.&lt;/p&gt;
&lt;p&gt;I wanted a solution that only relied on &lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&gt;, and started using &lt;a href=&quot;https://git-scm.com/docs/git-worktree&quot;&gt;worktrees&lt;/a&gt; to manage the Flutter channels.&lt;/p&gt;
&lt;h2&gt;Download the SDK&lt;/h2&gt;
&lt;p&gt;Check out the flutter repo in a known directory, in this case I will download it to &lt;code&gt;~/Developer/&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;git clone https://github.com/flutter/flutter ~/Developer/flutter
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Add Flutter Channels&lt;/h2&gt;
&lt;p&gt;Now we can add the branches we want to track:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd ~/Developer/flutter
git checkout origin/dev
git worktree add ../flutter-stable stable
git worktree add ../flutter-beta beta
git worktree add ../flutter-master master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to checkout the dev channel to allow us to create the worktree for the master branch. This will keep the &lt;code&gt;flutter&lt;/code&gt; directory separate so we can work on PRs and apply local changes.&lt;/p&gt;
&lt;p&gt;After this runs we should have 4 directories: &lt;code&gt;flutter&lt;/code&gt;, &lt;code&gt;flutter-master&lt;/code&gt;, &lt;code&gt;flutter-beta&lt;/code&gt; and &lt;code&gt;flutter-stable&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Add ZSH Alias for each Channel&lt;/h2&gt;
&lt;p&gt;Now we need a way to reference each SDK on the fly with an &lt;a href=&quot;https://github.com/rothgar/mastering-zsh/blob/master/docs/helpers/aliases.md&quot;&gt;alias in ZSH&lt;/a&gt;. Add the following to &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;alias flutter-master=&apos;~/Developer/flutter-master/bin/flutter&apos;
alias dart-master=&apos;~/Developer/flutter-master/bin/dart&apos;

alias flutter-beta=&apos;~/Developer/flutter-beta/bin/flutter&apos;
alias dart-beta=&apos;~/Developer/flutter-beta/bin/dart&apos;

alias flutter-stable=&apos;~/Developer/flutter-stable/bin/flutter&apos;
alias dart-stable=&apos;~/Developer/flutter-stable/bin/dart&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;After reopening the terminal, you can verify it is working by running (or add any channel we added above):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter-master doctor
dart-master --version

flutter-stable doctor
dart-stable --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can update any of the channels by navigating to the directory of the worktree for the given channel and pulling changes like any other Git repo.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd ~/Developer/flutter-master
git checkout origin/master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git worktrees are just a way to checkout multiple branches as separate folders instead of needing to stash changes.&lt;/p&gt;
</content:encoded></item><item><title>How to build a graph database with Flutter</title><link>https://rodydavis.com/flutter/graph-database/</link><guid isPermaLink="true">https://rodydavis.com/flutter/graph-database/</guid><description>Learn how to build and implement a graph database within a Flutter application for effectively modeling and managing complex data relationships.</description><pubDate>Sat, 18 Jan 2025 21:09:38 GMT</pubDate><content:encoded>&lt;h1&gt;How to build a graph database with Flutter&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to create and use a graph database with &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/flutter_graph_database&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/flutter_graph_database/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Flutter installed and setup (Refer to this &lt;a href=&quot;https://rodydavis.com/posts/first-flutter-project/&quot;&gt;article&lt;/a&gt; if you need help).&lt;/p&gt;
&lt;p&gt;Basic knowledge of &lt;a href=&quot;https://www.sqlite.org/index.html&quot;&gt;SQLite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Basic knowledge of Graph Databases (Refer to this &lt;a href=&quot;https://www.youtube.com/watch?v=GekQqFZm7mA&quot;&gt;video&lt;/a&gt; if you need to learn more).&lt;/p&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;First of all, why do we need a graph database when other storage options exist?&lt;/p&gt;
&lt;p&gt;Why not use key value stores, document stores, or relational databases?&lt;/p&gt;
&lt;p&gt;Well, the answer is that it depends on the problem you are trying to solve.&lt;/p&gt;
&lt;p&gt;Graph databases are great for modeling relationships between data.&lt;/p&gt;
&lt;p&gt;A couple examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A social network app can model the relationships between users and posts&lt;/li&gt;
&lt;li&gt;A game can model the relationships between players and items&lt;/li&gt;
&lt;li&gt;A blog can model the relationships between posts and comments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The possibilities are endless.&lt;/p&gt;
&lt;p&gt;Instead of storing data in a table for each collection we store the data as a graph in a nodes and edges table with some additional extensions in SQLite to make it easier.&lt;/p&gt;
&lt;p&gt;Here is a &lt;a href=&quot;https://www.hytradboi.com/2022/simple-graph-sqlite-as-probably-the-only-graph-database-youll-ever-need&quot;&gt;page&lt;/a&gt; that goes in to detail about it and showcases what we are trying to build.&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;First we need to create a new Flutter project.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir flutter_graph_database
cd flutter_graph_database
flutter create .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the project is created open it in your favorite code editor.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;code .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating the Database&lt;/h2&gt;
&lt;p&gt;We are going to use the &lt;a href=&quot;hhttps://pub.dev/packages/drift&quot;&gt;drift&lt;/a&gt; package to create the database.&lt;/p&gt;
&lt;p&gt;Update the &lt;strong&gt;pubspec.yaml&lt;/strong&gt; file with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;name: flutter_graph_database
description: A new Flutter package project.
version: 0.0.1
publish_to: none

environment:
  sdk: &amp;quot;&amp;gt;=2.19.0-238.0.dev &amp;lt;3.0.0&amp;quot;
  flutter: &amp;quot;&amp;gt;=1.17.0&amp;quot;

dependencies:
  flutter:
    sdk: flutter
  drift: ^2.1.0
  sqlite3_flutter_libs: ^0.5.5
  http: ^0.13.5
  path_provider: ^2.0.0
  path: ^1.8.2
  sqlite3: ^1.7.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0
  build_runner: ^2.2.0
  drift_dev: ^2.1.0

flutter:

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Database Connection&lt;/h3&gt;
&lt;p&gt;Next we need to create the database.&lt;/p&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/connection/unsupported.dart&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:drift/drift.dart&apos;;
import &apos;package:drift/native.dart&apos;;

DatabaseConnection connect(
  String dbName, {
  bool useWebWorker = false,
  bool logStatements = false,
}) {
  return DatabaseConnection(NativeDatabase.memory(
    logStatements: logStatements,
  ));
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/connection/native.dart&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:io&apos;;
import &apos;dart:isolate&apos;;

import &apos;package:drift/drift.dart&apos;;
import &apos;package:drift/isolate.dart&apos;;
import &apos;package:drift/native.dart&apos;;
import &apos;package:path_provider/path_provider.dart&apos;;
import &apos;package:path/path.dart&apos; as p;

DatabaseConnection connect(
  String dbName, {
  bool useWebWorker = false,
  bool logStatements = false,
}) {
  return DatabaseConnection.delayed(Future.sync(() async {
    final appDir = await getApplicationDocumentsDirectory();
    final dbPath = p.join(appDir.path, dbName);

    final receiveDriftIsolate = ReceivePort();
    await Isolate.spawn(_entrypointForDriftIsolate,
        _IsolateStartRequest(receiveDriftIsolate.sendPort, dbPath));

    final driftIsolate = await receiveDriftIsolate.first as DriftIsolate;
    return driftIsolate.connect();
  }));
}

class _IsolateStartRequest {
  final SendPort talkToMain;
  final String databasePath;

  _IsolateStartRequest(this.talkToMain, this.databasePath);
}

void _entrypointForDriftIsolate(_IsolateStartRequest request) {
  final databaseImpl = NativeDatabase(
    File(request.databasePath),
    logStatements: false,
  );

  final driftServer = DriftIsolate.inCurrent(
    () =&amp;gt; DatabaseConnection(databaseImpl),
  );

  request.talkToMain.send(driftServer);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/connection/web.dart&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;

// ignore: avoid_web_libraries_in_flutter
import &apos;dart:html&apos;;

import &apos;package:drift/drift.dart&apos;;
import &apos;package:drift/remote.dart&apos;;
import &apos;package:drift/web.dart&apos;;
import &apos;package:drift/wasm.dart&apos;;
import &apos;package:http/http.dart&apos; as http;
import &apos;package:sqlite3/wasm.dart&apos;;

DatabaseConnection connect(
  String dbName, {
  bool useWebWorker = false,
  bool logStatements = false,
}) {
  if (useWebWorker) {
    final worker = SharedWorker(&apos;shared_worker.dart.js&apos;);
    return remote(worker.port!.channel());
  } else {
    return DatabaseConnection.delayed(Future.sync(() async {
      final response = await http.get(Uri.parse(&apos;sqlite3.wasm&apos;));
      final fs = await IndexedDbFileSystem.open(dbName: &apos;/db/&apos;);
      final path = &apos;/drift/db/$dbName&apos;;
      final sqlite3 = await WasmSqlite3.load(
        response.bodyBytes,
        SqliteEnvironment(fileSystem: fs),
      );
      final databaseImpl = WasmDatabase(
        sqlite3: sqlite3,
        path: path,
        fileSystem: fs, // &amp;lt;- this is required but not documented
        logStatements: logStatements,
      );
      return DatabaseConnection(databaseImpl);
    }));
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/connection/connection.dart&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;export &apos;unsupported.dart&apos;
    if (dart.library.js) &apos;web.dart&apos;
    if (dart.library.ffi) &apos;native.dart&apos;;

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Database SQL Files&lt;/h3&gt;
&lt;h4&gt;Schema&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/schema.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE IF NOT EXISTS nodes (
    body TEXT,
    id   TEXT GENERATED ALWAYS AS (json_extract(body, &apos;$.id&apos;)) VIRTUAL NOT NULL UNIQUE
);

CREATE INDEX IF NOT EXISTS id_idx ON nodes(id);

CREATE TABLE IF NOT EXISTS edges (
    source     TEXT,
    target     TEXT,
    properties TEXT,
    UNIQUE(source, target, properties) ON CONFLICT REPLACE,
    FOREIGN KEY(source) REFERENCES nodes(id),
    FOREIGN KEY(target) REFERENCES nodes(id)
);

CREATE INDEX IF NOT EXISTS source_idx ON edges(source);
CREATE INDEX IF NOT EXISTS target_idx ON edges(target);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The ID column is a virtual column that is generated from the body column. This is done so that we can query the database by ID without having to parse the JSON body column.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Queries&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/queries.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

getAllNodes: 
    SELECT * FROM nodes;
getAllEdges: 
    SELECT * FROM edges;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Delete Edge&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/delete-edge.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

deleteEdge:
    DELETE FROM edges 
    WHERE source = ? OR target = ?;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Delete Node&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/delete-node.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

deleteNode: 
    DELETE FROM nodes 
    WHERE id = ?;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Insert Edge&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/insert-edge.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

insertEdge(:source as TEXT, :target as TEXT, :body as TEXT): 
    INSERT INTO edges VALUES(:source, :target, json(:body));
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Search Edges Inbound&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/search-edges-inbound.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

searchEdgesInbound: 
    SELECT * FROM edges 
    WHERE source = ?;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Search Edges Outbound&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/search-edges-outbound.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

searchEdgesOutbound: 
    SELECT * FROM edges 
    WHERE target = ?;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Search Edges&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/search-edges.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

searchEdges: 
    SELECT * FROM edges WHERE source = ? 
    UNION 
    SELECT * FROM edges WHERE target = ?;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Search Node By ID&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/search-node-by-id.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

searchNodeById: 
    SELECT body FROM nodes 
    WHERE id = ?;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Search Node&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/search-node.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

-- Create a text index of entries, see https://www.sqlite.org/fts5.html#external_content_tables
CREATE VIRTUAL TABLE node_entries USING fts5 (
    body,
    content=nodes,
    content_rowid=id
);

-- Triggers to keep entries and fts5 index in sync.
CREATE TRIGGER nodes_insert AFTER INSERT ON nodes BEGIN
  INSERT INTO node_entries(rowid, body) VALUES (new.id, new.body);
END;

CREATE TRIGGER nodes_delete AFTER DELETE ON nodes BEGIN
  INSERT INTO node_entries(node_entries, rowid, body) VALUES (&apos;delete&apos;, old.id, old.body);
END;

CREATE TRIGGER nodes_update AFTER UPDATE ON nodes BEGIN
  INSERT INTO node_entries(node_entries, rowid, body) VALUES (&apos;delete&apos;, new.id, new.body);
  INSERT INTO node_entries(rowid, body) VALUES (new.id, new.body);
END;

-- Full text search query.
searchNode: SELECT r.** FROM node_entries
    INNER JOIN nodes r ON r.id = node_entries.rowid
    WHERE node_entries MATCH :query
    ORDER BY rank;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Here we are using the &lt;a href=&quot;https://www.sqlite.org/fts5.html&quot;&gt;fts5&lt;/a&gt; extension to create a full text search index. This is a very powerful feature that allows us to search for nodes by their body text.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Traverse Inbound&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/traverse-inbound.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

traverseInbound(:source AS TEXT): 
  WITH RECURSIVE traverse(id) AS (
  SELECT :source
  UNION
  SELECT source FROM edges JOIN traverse ON target = id
) SELECT id FROM traverse;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Traverse Outbound&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/traverse-outbound.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

traverseOutbound(:source AS TEXT): 
  WITH RECURSIVE traverse(id) AS (
  SELECT :source
  UNION
  SELECT target FROM edges JOIN traverse ON source = id
) SELECT id FROM traverse;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Traverse Bodies Inbound&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/traverse-with-bodies-inbound.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

traverseWithBodiesInbound(:source AS TEXT): 
  WITH RECURSIVE traverse(x, y, obj) AS (
  SELECT :source, &apos;()&apos;, &apos;{}&apos;
  UNION
  SELECT id, &apos;()&apos;, body FROM nodes JOIN traverse ON id = x
  UNION
  SELECT source, &apos;&amp;lt;-&apos;, properties FROM edges JOIN traverse ON target = x
) SELECT x, y, obj FROM traverse;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Traverse Bodies Outbound&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/traverse-with-bodies-outbound.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

traverseWithBodiesOutbound(:source AS TEXT): 
  WITH RECURSIVE traverse(x, y, obj) AS (
  SELECT :source, &apos;()&apos;, &apos;{}&apos;
  UNION
  SELECT id, &apos;()&apos;, body FROM nodes JOIN traverse ON id = x
  UNION
  SELECT target, &apos;-&amp;gt;&apos;, properties FROM edges JOIN traverse ON source = x
) SELECT x, y, obj FROM traverse;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Traverse Bodies&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/traverse-bodies.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

traverseWithBodies(:source AS TEXT): 
  WITH RECURSIVE traverse(x, y, obj) AS (
  SELECT :source, &apos;()&apos;, &apos;{}&apos;
  UNION
  SELECT id, &apos;()&apos;, body FROM nodes JOIN traverse ON id = x
  UNION
  SELECT source, &apos;&amp;lt;-&apos;, properties FROM edges JOIN traverse ON target = x
  UNION
  SELECT target, &apos;-&amp;gt;&apos;, properties FROM edges JOIN traverse ON source = x
) SELECT x, y, obj FROM traverse;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Traverse&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/traverse.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

traverse(:source AS TEXT): 
  WITH RECURSIVE traverse(id) AS (
  SELECT :source
  UNION
  SELECT source FROM edges JOIN traverse ON target = id
  UNION
  SELECT target FROM edges JOIN traverse ON source = id
) SELECT id FROM traverse;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Update Node&lt;/h4&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/sql/update-node.drift&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;import &apos;schema.drift&apos;;

updateNode: 
    UPDATE nodes SET body = json(?) 
    WHERE id = ?;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Database Setup&lt;/h3&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/database/database.dart&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:convert&apos;;

import &apos;package:drift/drift.dart&apos;;
import &apos;package:flutter/foundation.dart&apos;;

import &apos;connection/connection.dart&apos; as impl;

part &apos;database.g.dart&apos;;

@DriftDatabase(include: {
  &apos;sql/schema.drift&apos;,
  &apos;sql/queries.drift&apos;,
  &apos;sql/delete-edge.drift&apos;,
  &apos;sql/delete-node.drift&apos;,
  &apos;sql/insert-edge.drift&apos;,
  &apos;sql/insert-node.drift&apos;,
  &apos;sql/search-edges-inbound.drift&apos;,
  &apos;sql/search-edges-outbound.drift&apos;,
  &apos;sql/search-edges.drift&apos;,
  &apos;sql/search-node-by-id.drift&apos;,
  &apos;sql/search-node.drift&apos;,
  &apos;sql/traverse-inbound.drift&apos;,
  &apos;sql/traverse-outbound.drift&apos;,
  &apos;sql/traverse-with-bodies-inbound.drift&apos;,
  &apos;sql/traverse-with-bodies-outbound.drift&apos;,
  &apos;sql/traverse-with-bodies.drift&apos;,
  &apos;sql/traverse.drift&apos;,
  &apos;sql/update-node.drift&apos;,
})
class GraphDatabase extends _$GraphDatabase {
  GraphDatabase({
    String dbName = &apos;graph_db.db&apos;,
    DatabaseConnection? connection,
    bool useWebWorker = false,
    bool logStatements = false,
  }) : super.connect(
          connection ??
              impl.connect(
                dbName,
                useWebWorker: useWebWorker,
                logStatements: logStatements,
              ),
        );

  @override
  int get schemaVersion =&amp;gt; 1;

  /// Helper method to add graph data from json
  Future&amp;lt;void&amp;gt; addGraphData(
    Map&amp;lt;String, dynamic&amp;gt; data, {
    bool shouldBatch = false,
  }) {
    return transaction(() async {
      try {
        final localNodes = data[&apos;nodes&apos;] as List&amp;lt;dynamic&amp;gt;;
        final localEdges = data[&apos;edges&apos;] as List&amp;lt;dynamic&amp;gt;;
        // Update nodes
        for (final node in localNodes) {
          final id = node[&apos;id&apos;] as String?;
          if (id != null) {
            final current = await searchNodeById(id).getSingleOrNull();
            final body = jsonEncode(node);
            if (current != null) {
              await updateNode(id, body);
            } else {
              await insertNode(body);
            }
          }
        }
        // Update edges
        for (final edge in localEdges) {
          final source = edge[&apos;from&apos;] ?? edge[&apos;source&apos;] as String?;
          final target = edge[&apos;to&apos;] ?? edge[&apos;target&apos;] as String?;
          if (source != null &amp;amp;&amp;amp; target != null) {
            final body = jsonEncode(edge);
            await insertEdge(source, target, body);
          }
        }
      } catch (e) {
        debugPrint(&apos;Error adding graph data: $e&apos;);
      }
    });
  }

  Future&amp;lt;void&amp;gt; deleteAll() {
    return transaction(() async {
      try {
        await deleteAllEdges();
        await deleteAllNodes();
      } catch (e) {
        debugPrint(&apos;Error clearing graph data: $e&apos;);
      }
    });
  }

  Future&amp;lt;void&amp;gt; deleteAllEdges() {
    return transaction(() async {
      final edges = await getAllEdges().get();
      for (final edge in edges) {
        await deleteEdge(edge.source, edge.target);
      }
    });
  }

  Future&amp;lt;void&amp;gt; deleteAllNodes() {
    return transaction(() async {
      final nodes = await getAllNodes().get();
      for (final node in nodes) {
        await deleteNode(node.id);
      }
    });
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;build.yaml&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;targets:
  $default:
    sources:
      - lib/**
      - web/**
      - &amp;quot;tool/**&amp;quot;
      - pubspec.yaml
      - lib/$lib$
      - $package$
    builders:
      drift_dev:
        options:
          sql:
            dialect: sqlite
            options:
              version: &amp;quot;3.38&amp;quot;
              modules:
                - json1
                - fts5
          generate_connect_constructor: true
          apply_converters_on_variables: true
          generate_values_in_copy_with: true
          scoped_dart_components: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now run the following command to generate the database files:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter pub run build_runner build --delete-conflicting-outputs
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Connecting to the Database&lt;/h2&gt;
&lt;p&gt;Add a new dependency to your &lt;strong&gt;pubspec.yaml&lt;/strong&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter pub add graphview
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will be used for the graph visualization.&lt;/p&gt;
&lt;p&gt;Create a new file at &lt;strong&gt;lib/main.dart&lt;/strong&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:convert&apos;;

import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter_graph_database/flutter_graph_database.dart&apos; as db;
import &apos;package:graphview/GraphView.dart&apos;;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Graph Database&apos;,
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark(),
      home: const Example(),
    );
  }
}

class Example extends StatefulWidget {
  const Example({Key? key}) : super(key: key);

  @override
  State&amp;lt;Example&amp;gt; createState() =&amp;gt; _ExampleState();
}

class _ExampleState extends State&amp;lt;Example&amp;gt; {
  final database = db.GraphDatabase();
  Graph graph = Graph();
  Algorithm builder = FruchtermanReingoldAlgorithm();

  final nodes = &amp;lt;String, db.Node&amp;gt;{};
  bool loaded = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) =&amp;gt; loadData());
  }

  @override
  void reassemble() {
    super.reassemble();
    // Needed to reset graph on hot reload
    loadData();
  }

  void setLoadedState(bool value) {
    if (mounted) {
      setState(() {
        loaded = value;
      });
    }
  }

  Future&amp;lt;void&amp;gt; addDummyData() async {
    // Load example data
    try {
      // Optionally reset data
      await database.deleteAll();
      // Add example data to database
      await database.addGraphData({
        &amp;quot;nodes&amp;quot;: [
          {&amp;quot;id&amp;quot;: &apos;1&apos;, &amp;quot;label&amp;quot;: &apos;circle&apos;},
          {&amp;quot;id&amp;quot;: &apos;2&apos;, &amp;quot;label&amp;quot;: &apos;ellipse&apos;},
          {&amp;quot;id&amp;quot;: &apos;3&apos;, &amp;quot;label&amp;quot;: &apos;database&apos;},
          {&amp;quot;id&amp;quot;: &apos;4&apos;, &amp;quot;label&amp;quot;: &apos;box&apos;},
          {&amp;quot;id&amp;quot;: &apos;5&apos;, &amp;quot;label&amp;quot;: &apos;diamond&apos;},
          {&amp;quot;id&amp;quot;: &apos;6&apos;, &amp;quot;label&amp;quot;: &apos;dot&apos;},
          {&amp;quot;id&amp;quot;: &apos;7&apos;, &amp;quot;label&amp;quot;: &apos;square&apos;},
          {&amp;quot;id&amp;quot;: &apos;8&apos;, &amp;quot;label&amp;quot;: &apos;triangle&apos;},
          {&amp;quot;id&amp;quot;: &apos;9&apos;, &amp;quot;label&amp;quot;: &amp;quot;star&amp;quot;},
        ],
        &amp;quot;edges&amp;quot;: [
          {&amp;quot;from&amp;quot;: &apos;1&apos;, &amp;quot;to&amp;quot;: &apos;2&apos;},
          {&amp;quot;from&amp;quot;: &apos;2&apos;, &amp;quot;to&amp;quot;: &apos;3&apos;},
          {&amp;quot;from&amp;quot;: &apos;2&apos;, &amp;quot;to&amp;quot;: &apos;4&apos;},
          {&amp;quot;from&amp;quot;: &apos;2&apos;, &amp;quot;to&amp;quot;: &apos;5&apos;},
          {&amp;quot;from&amp;quot;: &apos;5&apos;, &amp;quot;to&amp;quot;: &apos;6&apos;},
          {&amp;quot;from&amp;quot;: &apos;5&apos;, &amp;quot;to&amp;quot;: &apos;7&apos;},
          {&amp;quot;from&amp;quot;: &apos;6&apos;, &amp;quot;to&amp;quot;: &apos;8&apos;},
          {&amp;quot;from&amp;quot;: &apos;2&apos;, &amp;quot;to&amp;quot;: &apos;8&apos;},
          {&amp;quot;from&amp;quot;: &apos;1&apos;, &amp;quot;to&amp;quot;: &apos;8&apos;},
          {&amp;quot;from&amp;quot;: &apos;1&apos;, &amp;quot;to&amp;quot;: &apos;7&apos;},
          {&amp;quot;from&amp;quot;: &apos;1&apos;, &amp;quot;to&amp;quot;: &apos;6&apos;},
          {&amp;quot;from&amp;quot;: &apos;1&apos;, &amp;quot;to&amp;quot;: &apos;5&apos;},
          {&amp;quot;from&amp;quot;: &apos;1&apos;, &amp;quot;to&amp;quot;: &apos;4&apos;},
          {&amp;quot;from&amp;quot;: &apos;1&apos;, &amp;quot;to&amp;quot;: &apos;3&apos;},
          {&amp;quot;from&amp;quot;: &apos;1&apos;, &amp;quot;to&amp;quot;: &apos;9&apos;},
          {&amp;quot;from&amp;quot;: &apos;9&apos;, &amp;quot;to&amp;quot;: &apos;8&apos;},
          {&amp;quot;from&amp;quot;: &apos;9&apos;, &amp;quot;to&amp;quot;: &apos;5&apos;},
          {&amp;quot;from&amp;quot;: &apos;9&apos;, &amp;quot;to&amp;quot;: &apos;3&apos;},
        ]
      });
      loadData();
    } catch (e) {
      debugPrint(&apos;Error loading example data: $e&apos;);
    }
  }

  Future&amp;lt;void&amp;gt; loadData() async {
    setLoadedState(false);

    final nodeMap = &amp;lt;String, Node&amp;gt;{};
    this.nodes.clear();
    graph = Graph();
    builder = FruchtermanReingoldAlgorithm();

    // Load graph data
    final nodes = await database.getAllNodes().get();
    final edges = await database.getAllEdges().get();

    for (final node in nodes) {
      final newNode = Node.Id(node.id);
      nodeMap[node.id] = newNode;
      this.nodes[node.id] = node;
      graph.addNode(newNode);
    }
    for (final edge in edges) {
      final source = nodeMap[edge.source];
      final target = nodeMap[edge.target];
      if (source != null &amp;amp;&amp;amp; target != null) {
        graph.addEdge(source, target);
      }
    }

    setLoadedState(true);
  }

  Widget buildNode(Node node) {
    final dbNode = nodes[node.key!.value];
    final data = jsonDecode(dbNode?.body ?? &apos;{}&apos;) as Map&amp;lt;String, dynamic&amp;gt;;
    final label = data[&apos;label&apos;] ?? &apos;&apos;;
    return SizedBox(
      width: 80,
      height: 80,
      child: Center(
        child: Text(
          label,
          textAlign: TextAlign.center,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(&apos;Flutter Graph Database&apos;),
        actions: [
          IconButton(
            icon: const Icon(Icons.restore),
            onPressed: addDummyData,
          ),
        ],
      ),
      body: !loaded
          ? const Center(child: CircularProgressIndicator())
          : nodes.isEmpty
              ? const Center(child: Text(&apos;No Data Loaded&apos;))
              : LayoutBuilder(builder: (context, dimens) {
                  return SizedBox.expand(
                    child: InteractiveViewer(
                      constrained: false,
                      boundaryMargin: EdgeInsets.symmetric(
                        horizontal: dimens.maxWidth * 0.75,
                        vertical: dimens.maxHeight * 0.75,
                      ),
                      minScale: 0.01,
                      maxScale: 5.6,
                      child: GraphView(
                        key: UniqueKey(),
                        graph: graph,
                        algorithm: builder,
                        paint: Paint()
                          ..color = Colors.green
                          ..strokeWidth = 1
                          ..style = PaintingStyle.stroke,
                        builder: buildNode,
                      ),
                    ),
                  );
                }),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you run the flutter app you should see the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/graph_flutter_final_yikh8roctq.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building a graph database in Flutter, check out the &lt;a href=&quot;https://github.com/rodydavis/flutter_graph_database&quot;&gt;source code&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Lit and Flutter</title><link>https://rodydavis.com/flutter/lit-interop/</link><guid isPermaLink="true">https://rodydavis.com/flutter/lit-interop/</guid><description>Learn how to integrate Lit web components into Flutter, enabling developers to embed existing web applications into cross-platform mobile apps while accessing device APIs and handling updates seamlessly.</description><pubDate>Mon, 20 Jan 2025 02:22:00 GMT</pubDate><content:encoded>&lt;h1&gt;Lit and Flutter&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it inline in the Flutter widget tree.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; You can find the final source &lt;a href=&quot;https://github.com/rodydavis/flutter_hybrid_template&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The reason you would want this integration is so you can take an existing web app, or just a single part of it and embed it in the widget tree.&lt;/p&gt;
&lt;p&gt;With it wrapped in Flutter you can call device APIs from event listeners on your web component.&lt;/p&gt;
&lt;p&gt;For example you may have an app that handles purchases, and now you can call the in app purchase API or other device specific features not available on the web.&lt;/p&gt;
&lt;p&gt;You also get a cross platform app that can be delivered to both Google Play and the App Store.&lt;/p&gt;
&lt;p&gt;The web component will receive new code each time you update your site, so you do not have to ship an update each time the web component changes.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Flutter SDK&lt;/li&gt;
&lt;li&gt;Xcode and Command Line Tools&lt;/li&gt;
&lt;li&gt;Android SDK&lt;/li&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by creating a empty directory and naming it with &lt;code&gt;snake_case&lt;/code&gt; whatever we want.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir flutter_lit_example
cd flutter_lit_example
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Web Setup&lt;/h3&gt;
&lt;p&gt;Now we are in the &lt;code&gt;flutter_lit_example&lt;/code&gt; directory and can setup Flutter and Lit. Let&apos;s start with node.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init -y
npm i lit
npm i -D typescript vite @types/node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will setup the basics for a node project and install the packages we need. Now lets add some config files.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;touch tsconfig.json
touch vite.config.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create 2 files. Now open up &lt;code&gt;tsconfig.json&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &amp;quot;compilerOptions&amp;quot;: {
    &amp;quot;module&amp;quot;: &amp;quot;esnext&amp;quot;,
    &amp;quot;lib&amp;quot;: [
      &amp;quot;es2017&amp;quot;,
      &amp;quot;dom&amp;quot;,
      &amp;quot;dom.iterable&amp;quot;
    ],
    &amp;quot;types&amp;quot;: [
      &amp;quot;vite/client&amp;quot;
    ],
    &amp;quot;declaration&amp;quot;: true,
    &amp;quot;emitDeclarationOnly&amp;quot;: true,
    &amp;quot;outDir&amp;quot;: &amp;quot;./types&amp;quot;,
    &amp;quot;rootDir&amp;quot;: &amp;quot;./src&amp;quot;,
    &amp;quot;strict&amp;quot;: true,
    &amp;quot;noUnusedLocals&amp;quot;: true,
    &amp;quot;noUnusedParameters&amp;quot;: true,
    &amp;quot;noImplicitReturns&amp;quot;: true,
    &amp;quot;noFallthroughCasesInSwitch&amp;quot;: true,
    &amp;quot;moduleResolution&amp;quot;: &amp;quot;node&amp;quot;,
    &amp;quot;allowSyntheticDefaultImports&amp;quot;: true,
    &amp;quot;experimentalDecorators&amp;quot;: true,
    &amp;quot;forceConsistentCasingInFileNames&amp;quot;: true
  },
  &amp;quot;include&amp;quot;: [
    &amp;quot;src/**/*.ts&amp;quot;
  ],
  &amp;quot;exclude&amp;quot;: []
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a basic typescript config. Now open up &lt;code&gt;vite.config.ts&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

// https://vitejs.dev/config/
export default defineConfig({
  base: &amp;quot;/flutter_lit_example/&amp;quot;, // TODO: Name of your github repo
  build: {
    outDir: &amp;quot;build/web&amp;quot;,
    rollupOptions: {
      output: {
        entryFileNames: `assets/[name].js`,
        chunkFileNames: `assets/[name].js`,
        assetFileNames: `assets/[name].[ext]`,
      },
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
        // TODO: Create a new module for each component you want to embed
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to create our web component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir src
cd src
touch my-app.ts
cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;my-app.ts&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;

@customElement(&amp;quot;my-app&amp;quot;)
export class MyApp extends LitElement {
  static styles = css`
    p {
      color: blue;
    }
  `;

  @property()
  name = &amp;quot;Somebody&amp;quot;;

  render() {
    return html`&amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Hello, ${this.name}!&amp;lt;/p&amp;gt;
      &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;/div&amp;gt;`;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to create a &lt;code&gt;index.html&lt;/code&gt; for our web app.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;touch index.html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;index.html&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/my-app.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        padding: 0;
        margin: 0;
      }
      my-app {
        width: 100%;
        height: 100vh;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;my-app&amp;gt;&amp;lt;/my-app&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Flutter Setup&lt;/h3&gt;
&lt;p&gt;Now that we have the basics setup for web we can move on to flutter. Let&apos;s create the project with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter create --platforms=ios,android .
flutter packages get
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open up &lt;code&gt;pubspec.yaml&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;name: flutter_lit_example
description: A hybrid Flutter app.
publish_to: &amp;quot;none&amp;quot;
version: 1.0.0+1

environment:
  sdk: &amp;quot;&amp;gt;=2.7.0 &amp;lt;3.0.0&amp;quot;

dependencies:
  flutter:
    sdk: flutter
  flutter_inappwebview: ^5.3.2

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to get the packages again:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter packages get
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to create the file that will wrap the web component.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd lib
touch web_component.dart
cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;web_component.dart&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter_inappwebview/flutter_inappwebview.dart&apos;;

class WebComponent extends StatefulWidget {
  const WebComponent({
    Key key,
    @required this.name,
    @required this.bundle,
    this.attributes = const {},
    this.slot = &apos;&apos;,
    this.events = const [],
  }) : super(key: key);
  final String name, bundle;
  final Map&amp;lt;String, String&amp;gt; attributes;
  final String slot;
  final List&amp;lt;EventCallback&amp;gt; events;

  @override
  _WebComponentState createState() =&amp;gt; _WebComponentState();
}

class _WebComponentState extends State&amp;lt;WebComponent&amp;gt; {
  InAppWebViewController controller;
  final Map&amp;lt;String, List&amp;lt;EventCallback&amp;gt;&amp;gt; _events = {};

  String get source {
    return &apos;&apos;&apos;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        padding: 0;
        margin: 0;
      }
      ${widget.name} {
        width: 100%;
        height: 100vh;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;script type=&amp;quot;module&amp;quot; crossorigin src=&amp;quot;${widget.bundle}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;${widget.name} ${widget.attributes.entries.map((e) =&amp;gt; &apos;${e.key}=&amp;quot;${e.value}&amp;quot;&apos;).join(&apos; &apos;)}&amp;gt;
      ${widget.slot}
    &amp;lt;/${widget.name}&amp;gt;
    &amp;lt;script&amp;gt;
    window.addEventListener(&amp;quot;flutterInAppWebViewPlatformReady&amp;quot;, (event) =&amp;gt; {
      ${widget.events.join(&apos;\n&apos;)}
    });
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt; 
&apos;&apos;&apos;;
  }

  void _setup(InAppWebViewController controller) {
    this.controller = controller;
    this._setupEvents();
  }

  void _setupEvents() {
    for (final event in _events.keys) {
      controller.removeJavaScriptHandler(handlerName: event);
    }
    for (final event in widget.events) {
      _addEvent(event);
    }
  }

  void _addEvent(EventCallback event) {
    controller.addJavaScriptHandler(
      handlerName: event.query,
      callback: event.onPressed,
    );
    _events[event.event] ??= [];
    _events[event.event].add(event);
  }

  @override
  void didUpdateWidget(covariant WebComponent oldWidget) {
    if (oldWidget.events != widget.events) {
      _setupEvents();
    }
    if (oldWidget.slot != widget.slot ||
        oldWidget.bundle != widget.bundle ||
        oldWidget.name != widget.name) {
      controller.loadData(data: source);
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return InAppWebView(
      initialData: InAppWebViewInitialData(data: source),
      onWebViewCreated: _setup,
    );
  }
}

class EventCallback {
  EventCallback({
    @required this.onPressed,
    @required this.event,
    this.query,
  });
  final String query, event;
  final dynamic Function(List&amp;lt;dynamic&amp;gt; args) onPressed;

  @override
  String toString() =&amp;gt; _source;

  String get _prefix =&amp;gt; query != null &amp;amp;&amp;amp; query.isNotEmpty
      ? &apos;document.querySelector(&amp;quot;$query&amp;quot;)&apos;
      : &apos;document.body&apos;;

  String get _source =&amp;gt; [
        &apos;$_prefix.addEventListener(&amp;quot;$event&amp;quot;, (e) =&amp;gt; {&apos;,
        &apos;  window.flutter_inappwebview.callHandler(&amp;quot;$query&amp;quot;, e);&apos;,
        &apos;}, false);&apos;,
      ].join(&apos;\n&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;main.dart&lt;/code&gt; and paste it with th following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;web_component.dart&apos;;

const WEBSITE_URL = &apos;https://rodydavis.github.io/flutter_lit_example/&apos;;
const BUNDLE_PATH = &apos;assets/main.js&apos;;

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final title = &apos;Flutter Hybrid App&apos;;
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: title,
      theme: ThemeData(primarySwatch: Colors.blue),
      home: MyHomePage(title: title),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Builder(
        builder: (context) =&amp;gt; WebComponent(
          name: &apos;my-app&apos;,
          bundle: &apos;$WEBSITE_URL/$BUNDLE_PATH&apos;,
          attributes: {
            &apos;name&apos;: widget.title,
          },
          slot: &apos;&amp;lt;button id=&amp;quot;my-button&amp;quot;&amp;gt;Talk back!&amp;lt;/button&amp;gt;&apos;,
          events: [
            EventCallback(
              event: &apos;click&apos;,
              query: &apos;#my-button&apos;,
              onPressed: (_) {
                ScaffoldMessenger.of(context)
                    .showSnackBar(SnackBar(content: Text(&apos;Clicked!&apos;)));
              },
            ),
          ],
        ),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will need to update &lt;code&gt;WEBSITE_URL&lt;/code&gt; to have the url of the website where you will be deploying and &lt;code&gt;BUNDLE_URL&lt;/code&gt; to the relative path to the js bundle.&lt;/p&gt;
&lt;p&gt;This will ensure auto updates with a new version rolls out and the cache it stale. This will also allow for offline support after the first time it is downloaded.&lt;/p&gt;
&lt;h2&gt;Running&lt;/h2&gt;
&lt;p&gt;Now we can run our application but it requires a few steps to get it all setup.&lt;/p&gt;
&lt;p&gt;To test and build our web app locally we will use &lt;a href=&quot;https://github.com/vitejs/vite&quot;&gt;vite&lt;/a&gt; and render the &lt;code&gt;index.html&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm i
npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;vite v2.2.3 dev server running at:

Local:    http://localhost:3000/flutter_lit_example/
Network:  http://192.168.1.143:3000/flutter_lit_example/

ready in 311ms.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can open the link &lt;code&gt;http://localhost:3000/flutter_lit_example/&lt;/code&gt; to see our running web app and hot reload changes from &lt;code&gt;my-app.ts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to learn more about Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you are happy with how it looks we can move on to Flutter to wrap it in a native app. This will give us access to native code if we wanted to use the in app purchase api or push notifications.&lt;/p&gt;
&lt;p&gt;Kill the terminal and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter packages get
flutter build ios
flutter build appbundle
flutter run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should select a running device or prompt you to select one. Now that it is running on the device you can see we have two way communication with the Flutter app and the web component.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to find the source code you can check it out &lt;a href=&quot;https://github.com/rodydavis/flutter_hybrid_template&quot;&gt;here&lt;/a&gt; otherwise thanks for reading and let me know if you have any questions!&lt;/p&gt;
</content:encoded></item><item><title>Host your Flutter Project as a REST API</title><link>https://rodydavis.com/flutter/host-rest-api/</link><guid isPermaLink="true">https://rodydavis.com/flutter/host-rest-api/</guid><description>Learn how to use a single Flutter codebase to expose your Dart models and business logic as a REST API, enabling unified development for both client and server applications.</description><pubDate>Mon, 20 Jan 2025 00:30:40 GMT</pubDate><content:encoded>&lt;h1&gt;Host your Flutter Project as a REST API&lt;/h1&gt;
&lt;p&gt;After you build your flutter project you may want to reuse the models and business logic from your lib folder. I will show you how to go about setting up the project to have iOS, Android, Web, Windows, MacOS, Linux and a REST API interface with one project. The REST API can also be deploy to Google Cloud Run for Dart everywhere.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_1_gl7e2erkta.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One Codebase for Client and Sever.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This will allow you to expose your Dart models as a REST API and run your business logic from your lib folder while the application runs the models as they are. &lt;a href=&quot;https://github.com/rodydavis/shared_dart&quot;&gt;Here&lt;/a&gt; is the final project.&lt;/p&gt;
&lt;h2&gt;Setting Up&lt;/h2&gt;
&lt;p&gt;As with any Flutter project I am going to assume that you already have &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; installed on your machine and that you can create a project. This is a intermediate level difficulty so read on if you are up to the challenge. You will also need to know the basics of &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Why one project?&lt;/h2&gt;
&lt;p&gt;It may not be obvious but when building complex applications you will at some point have a server and an application that calls that server. &lt;a href=&quot;https://firebase.google.com/&quot;&gt;Firebase&lt;/a&gt; is an excellent option for doing this and I use it in almost all my projects. &lt;a href=&quot;https://firebase.google.com/products/functions/&quot;&gt;Firebase Functions&lt;/a&gt; are really powerful but you are limited by Javascript or Typescript. What if you could use the same packages that you are using in the Flutter project, or better yet what if they both used the same?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_2_t53hqci5ox.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;When you have a server project and a client project that communicate over a rest api or client sdk like Firebase then you will run into the problem that the server has models of objects stored and the client has models of the objects that are stored. This can lead to a serious mismatch when it changed without you knowing. GraphQL helps a lot with this since you define the model that you recieve. This approach allows your business logic to be always up to date for both the client and server.&lt;/p&gt;
&lt;h2&gt;Client Setup&lt;/h2&gt;
&lt;p&gt;The first step is to just build your application. The only difference that we will make is keeping the UI and business logic separate. When starting out with Flutter it can be very easy to throw all the logic into the screen and calling setState when the data changes. Even the application when creating a new Flutter project does this. That&apos;s why &lt;a href=&quot;https://flutter.dev/docs/development/data-and-backend/state-mgmt/options&quot;&gt;choosing a state management solution&lt;/a&gt; is so important.&lt;/p&gt;
&lt;p&gt;To make things clean and concise we will make 2 folders in our lib folder.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ui for all Flutter Widgets and Screens&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;src for all business logic, classes, models and utility functions&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This will leave us with main.dart being only the entry point into our client application.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;plugins/desktop/desktop.dart&apos;;
import &apos;ui/home/screen.dart&apos;;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      home: HomeScreen(),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s Start by making a tab bar for the 2 screens. Create a file in the folder ui/home/screen.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;../counter/screen.dart&apos;;
import &apos;../todo/screen.dart&apos;;

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  int _currentIndex = 0;

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: &amp;lt;Widget&amp;gt;[
          CounterScreen(),
          TodosScreen(),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (val) {
          if (mounted)
            setState(() {
              _currentIndex = val;
            });
        },
        type: BottomNavigationBarType.fixed,
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.add),
            title: Text(&apos;Counter&apos;),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.list),
            title: Text(&apos;Todos&apos;),
          ),
        ],
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is just a basic screen and should look very normal.&lt;/p&gt;
&lt;h3&gt;Counter Example&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_3_pwj25p3nfr.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now create a file ui/counter/screen.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:shared_dart/src/models/counter.dart&apos;;

class CounterScreen extends StatefulWidget {
  @override
  _CounterScreenState createState() =&amp;gt; _CounterScreenState();
}

class _CounterScreenState extends State&amp;lt;CounterScreen&amp;gt; {
  CounterModel _counterModel = CounterModel();

void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counterModel.add();
    });
  }

@override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyCounterPage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(&apos;Counter Screen&apos;),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke &amp;quot;debug painting&amp;quot; (press &amp;quot;p&amp;quot; in the console, choose the
          // &amp;quot;Toggle Debug Paint&amp;quot; action from the Flutter Inspector in Android
          // Studio, or the &amp;quot;Toggle Debug Paint&amp;quot; command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            Text(
              &apos;You have pushed the button this many times:&apos;,
            ),
            Text(
              &apos;${_counterModel.count}&apos;,
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the default counter app you get when you create a Flutter application but with one change, it uses &lt;code&gt;CounterModel&lt;/code&gt; to hold the logic.&lt;/p&gt;
&lt;p&gt;Create the counter model at src/models/counter.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;class CounterModel {
  CounterModel();

  int _count = 0;

  int get count =&amp;gt; _count;

  void add() =&amp;gt; _count++;

  void subtract() =&amp;gt; _count--;

  void set(int val) =&amp;gt; _count = val;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see it is really easy to expose only what we want to while still having complete flexibility. You could use provider here if you choose, or even bloc and/or streams.&lt;/p&gt;
&lt;h3&gt;Todo Example&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_4_4cia0ajhj0.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Lets create a file at ui/todos/screen.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;../../src/classes/todo.dart&apos;;
import &apos;../../src/models/todos.dart&apos;;

class TodosScreen extends StatefulWidget {
  @override
  _TodosScreenState createState() =&amp;gt; _TodosScreenState();
}

class _TodosScreenState extends State&amp;lt;TodosScreen&amp;gt; {
  final _model = TodosModel();
  List&amp;lt;ToDo&amp;gt; _todos;

@override
  void initState() {
    _model.getList().then((val) {
      if (mounted)
        setState(() {
          _todos = val;
        });
    });
    super.initState();
  }

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(&apos;Todos Screen&apos;),
      ),
      body: Builder(
        builder: (_) {
          if (_todos != null) {
            return ListView.builder(
              itemCount: _todos.length,
              itemBuilder: (context, index) {
                final _item = _todos[index];
                return ListTile(
                  title: Text(_item.title),
                  subtitle: Text(_item.completed ? &apos;Completed&apos; : &apos;Pending&apos;),
                );
              },
            );
          }
          return Center(
            child: CircularProgressIndicator(),
          );
        },
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see that we have the logic in TodosModel and uses the class ToDo for toJson and fromJson.&lt;/p&gt;
&lt;p&gt;Create a file at the location src/classes/todo.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;// To parse this JSON data, do
//
//     final toDo = toDoFromJson(jsonString);

import &apos;dart:convert&apos;;

List&amp;lt;ToDo&amp;gt; toDoFromJson(String str) =&amp;gt; List&amp;lt;ToDo&amp;gt;.from(json.decode(str).map((x) =&amp;gt; ToDo.fromJson(x)));

String toDoToJson(List&amp;lt;ToDo&amp;gt; data) =&amp;gt; json.encode(List&amp;lt;dynamic&amp;gt;.from(data.map((x) =&amp;gt; x.toJson())));

class ToDo {
    int userId;
    int id;
    String title;
    bool completed;

ToDo({
        this.userId,
        this.id,
        this.title,
        this.completed,
    });

factory ToDo.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) =&amp;gt; ToDo(
        userId: json[&amp;quot;userId&amp;quot;],
        id: json[&amp;quot;id&amp;quot;],
        title: json[&amp;quot;title&amp;quot;],
        completed: json[&amp;quot;completed&amp;quot;],
    );

Map&amp;lt;String, dynamic&amp;gt; toJson() =&amp;gt; {
        &amp;quot;userId&amp;quot;: userId,
        &amp;quot;id&amp;quot;: id,
        &amp;quot;title&amp;quot;: title,
        &amp;quot;completed&amp;quot;: completed,
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and create the model src/models/todo.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:convert&apos;;

import &apos;package:http/http.dart&apos; as http;
import &apos;package:shared_dart/src/classes/todo.dart&apos; as t;

class TodosModel {
  final kTodosUrl = &apos;[https://jsonplaceholder.typicode.com/todos&apos;](https://jsonplaceholder.typicode.com/todos&apos;);

Future&amp;lt;List&amp;lt;t.ToDo&amp;gt;&amp;gt; getList() async {
    final _response = await http.get(kTodosUrl);
    if (_response != null) {
      final _todos = t.toDoFromJson(_response.body);
      if (_todos != null) {
        return _todos;
      }
    }
    return [];
  }

Future&amp;lt;t.ToDo&amp;gt; getItem(int id) async {
    final _response = await http.get(&apos;$kTodosUrl/$id&apos;);
    if (_response != null) {
      final _todo = t.ToDo.fromJson(json.decode(_response.body));
      if (_todo != null) {
        return _todo;
      }
    }
    return null;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we just get dummy data from a url that emits json and convert them to our classes. This is an example I want to show with networking. There is only one place that fetches the data.&lt;/p&gt;
&lt;h3&gt;Run the Project (Web)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_5_cpppxwnavj.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_6_7bhqlwxjfs.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see when you run your project on chrome you will get the same application that you got on mobile. Even the networking is working in the web. You can call the model and retrieve the list just like you would expect.&lt;/p&gt;
&lt;h2&gt;Server Setup&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Now time for the magic..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the root of the project folder create a file Dockerfile and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# Use Google&apos;s official Dart image.
# [https://hub.docker.com/r/google/dart-runtime/](https://hub.docker.com/r/google/dart-runtime/)
FROM google/dart-runtime
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create another file at the root called service.yaml and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: PROJECT_NAME
      namespace: default
    spec:
      template:
        spec:
          containers:
            - image: docker.io/YOUR_DOCKER_NAME/PROJECT_NAME
              env:
                - name: TARGET
                  value: &amp;quot;PROJECT_NAME v1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace PROJECT_NAME with your project name, mine is shared-dart for this example.&lt;/p&gt;
&lt;p&gt;You will also need to replace YOUR_DOCKER_NAME with your docker username so the container can be deployed correctly.&lt;/p&gt;
&lt;p&gt;Update your pubspec.yaml with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;name: shared_dart
description: A new Flutter project.
publish_to: none
version: 1.0.0+1

environment:
  sdk: &amp;quot;&amp;gt;=2.1.0 &amp;lt;3.0.0&amp;quot;

dependencies:
  flutter:
    sdk: flutter
  shelf: ^0.7.3
  cupertino_icons: ^0.1.2
  http: ^0.12.0+2

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important package here is shelf as it allows us to run a http server with dart.&lt;/p&gt;
&lt;p&gt;Create a folder in the root of the project called bin then add a file server.dart and replace it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:io&apos;;

import &apos;package:shelf/shelf.dart&apos; as shelf;
import &apos;package:shelf/shelf_io.dart&apos; as io;

import &apos;src/routing.dart&apos;;

void main() {
  final handler = const shelf.Pipeline()
      .addMiddleware(shelf.logRequests())
      .addHandler(RouteUtils.handler);

final port = int.tryParse(Platform.environment[&apos;PORT&apos;] ?? &apos;8080&apos;);
  final address = InternetAddress.anyIPv4;

io.serve(handler, address, port).then((server) {
    server.autoCompress = true;
    print(&apos;Serving at [http://${server.address.host}:${server.port}&apos;](http://${server.address.host}:${server.port}&apos;));
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will tell the container what port to listen for and how to handle the requests.&lt;/p&gt;
&lt;p&gt;Create a folder src in the bin folder and add a file routing.dart and replace the contents with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;

import &apos;package:shelf/shelf.dart&apos; as shelf;

import &apos;controllers/index.dart&apos;;
import &apos;result.dart&apos;;

class RouteUtils {
  static FutureOr&amp;lt;shelf.Response&amp;gt; handler(shelf.Request request) {
    var component = request.url.pathSegments.first;
    var handler = _handlers(request)[component];
    if (handler == null) return shelf.Response.notFound(null);
    return handler;
  }

static Map&amp;lt;String, FutureOr&amp;lt;shelf.Response&amp;gt;&amp;gt; _handlers(
      shelf.Request request) {
    return {
      &apos;info&apos;: ServerResponse(&apos;Info&apos;, body: {
        &amp;quot;version&amp;quot;: &apos;v1.0.0&apos;,
        &amp;quot;status&amp;quot;: &amp;quot;ok&amp;quot;,
      }).ok(),
      &apos;counter&apos;: CounterController().result(request),
      &apos;todos&apos;: TodoController().result(request),
    };
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is still nothing imported from our main project but you will start to see some similarities. Here we specify controllers for todos and counter url paths.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;&apos;counter&apos;: CounterController().result(request),
&apos;todos&apos;: TodoController().result(request),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;that means any url with the following:&lt;a href=&quot;https://mydomain.com/todos&quot;&gt;https://mydomain.com/todos&lt;/a&gt; , &lt;a href=&quot;https://mydomain.com/todos&quot;&gt;https://mydomain.com/todos&lt;/a&gt;/1&lt;/p&gt;
&lt;p&gt;will get routed to the TodoController to handle the request.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is also the first time I found out about FutureOr. It allows you to return a sync or async function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And important part about build a REST API is having a consistent response body, so here we can create a wrapper that adds fields we always want to return, like the status of the call, a message and the body.&lt;/p&gt;
&lt;p&gt;Create a file at src/result.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:convert&apos;;

import &apos;package:shelf/shelf.dart&apos; as shelf;

class ServerResponse {
  final String message;
  final dynamic body;
  final StatusType type;

ServerResponse(
    this.message, {
    this.type = StatusType.success,
    this.body,
  });

Map&amp;lt;String, dynamic&amp;gt; toJson() {
    return {
      &amp;quot;status&amp;quot;: type.toString().replaceAll(&apos;StatusType.&apos;, &apos;&apos;),
      &amp;quot;message&amp;quot;: message,
      &amp;quot;body&amp;quot;: body ?? &apos;&apos;,
    };
  }

String toJsonString() {
    return json.encode(toJson());
  }

shelf.Response ok() {
    return shelf.Response.ok(
      toJsonString(),
      headers: {
        &apos;Content-Type&apos;: &apos;application/json&apos;,
      },
    );
  }
}

enum StatusType { success, error }

abstract class ResponseImpl {
  Future&amp;lt;shelf.Response&amp;gt; result(shelf.Request request);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will always return json and the fields that we want to show. You could also include your paging meta data here.&lt;/p&gt;
&lt;p&gt;Create a file in at the location src/controllers/counter.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:shared_dart/src/models/counter.dart&apos;;
import &apos;package:shelf/shelf.dart&apos; as shelf;

import &apos;../result.dart&apos;;

class CounterController implements ResponseImpl {
  const CounterController();

@override
  Future&amp;lt;shelf.Response&amp;gt; result(shelf.Request request) async {
    final _model = CounterModel();
    final _params = request.url.queryParameters;
    if (_params != null) {
      final _val = int.tryParse(_params[&apos;count&apos;] ?? &apos;0&apos;);
      _model.set(_val);
    } else {
      _model.add();
    }
    return ServerResponse(&apos;Info&apos;, body: {
      &amp;quot;counter&amp;quot;: _model.count,
    }).ok();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the import to the lib folder of the root project. Since it shares the pubspec.yaml all the packages can be shared. You can import the CounterModel that we created earlier.&lt;/p&gt;
&lt;p&gt;Create a file in at the location src/controllers/todos.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:shared_dart/src/models/todos.dart&apos;;
import &apos;package:shelf/src/request.dart&apos;;

import &apos;package:shelf/src/response.dart&apos;;

import &apos;../result.dart&apos;;

class TodoController implements ResponseImpl {
  @override
  Future&amp;lt;Response&amp;gt; result(Request request) async {
    final _model = TodosModel();
    if (request.url.pathSegments.length &amp;gt; 1) {
      final _id = int.tryParse(request.url.pathSegments[1] ?? &apos;1&apos;);
      final _todo = await _model.getItem(_id);
      return ServerResponse(&apos;Todo Item&apos;, body: _todo).ok();
    }
    final _todos = await _model.getList();
    return ServerResponse(
      &apos;List Todos&apos;,
      body: _todos.map((t) =&amp;gt; t.toJson()).toList(),
    ).ok();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like before we are importing the TodosModel model from the lib folder.&lt;/p&gt;
&lt;p&gt;For convenience add a file at the location src/controllers/index.dart and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;export &apos;counter.dart&apos;;
export &apos;todo.dart&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will make it easier to import all the controllers.&lt;/p&gt;
&lt;h2&gt;Run the Project (Server)&lt;/h2&gt;
&lt;p&gt;If you are using &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VSCode&lt;/a&gt; then you will need to update your launch.json with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: [https://go.microsoft.com/fwlink/?linkid=830387](https://go.microsoft.com/fwlink/?linkid=830387)
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,
    &amp;quot;configurations&amp;quot;: [
        {
            &amp;quot;name&amp;quot;: &amp;quot;Client&amp;quot;,
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;dart&amp;quot;,
            &amp;quot;program&amp;quot;: &amp;quot;lib/main.dart&amp;quot;
        }, 
          {
            &amp;quot;name&amp;quot;: &amp;quot;Server&amp;quot;,
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;dart&amp;quot;,
            &amp;quot;program&amp;quot;: &amp;quot;bin/server.dart&amp;quot;
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you hit run with Server selected you will see the output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_7_fph074ovtl.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can navigate to this in a browser but you can also work with this in &lt;a href=&quot;https://www.getpostman.com/&quot;&gt;Postman&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_8_3jwm0aouc0.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_9_ry6xjpbpbx.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Just by adding to the url todos and todos/1 it will return different responses.&lt;/p&gt;
&lt;p&gt;For the counter model we can use query parameters too!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_10_ec9e6yhbxc.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_11_puha19uiln.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Just by adding ?count=22 it will update the model with the input.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Keep in mind this is running your Dart code from you lib folder in your Flutter project without needing the Flutter widgets!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As a side benefit we can also run this project on Desktop. Check out the final project for the desktop folders needed from &lt;a href=&quot;https://github.com/google/flutter-desktop-embedding&quot;&gt;Flutter Desktop Embedding&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_12_2rv9jnqeo0.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_13_k7amx8s1d5.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now if you wanted to deploy the container to Cloud Run you could with the following command:&lt;/p&gt;
&lt;p&gt;gcloud builds submit — tag gcr.io/YOUR_GOOGLE_PROJECT_ID/PROJECT_NAME .&lt;/p&gt;
&lt;p&gt;Replace PROJECT_NAME with your project name, mine is shared-dart for this example.&lt;/p&gt;
&lt;p&gt;You will also need to replace YOUR_GOOGLE_PROJECT_ID with your Google Cloud Project ID. You can create one &lt;a href=&quot;https://cloud.google.com/cloud-build/docs/quickstart-docker&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Again the final project source code is &lt;a href=&quot;https://github.com/rodydavis/shared_dart&quot;&gt;here&lt;/a&gt;. Let me know your thoughts!&lt;/p&gt;
</content:encoded></item><item><title>Multi-touch Canvas with Flutter</title><link>https://rodydavis.com/flutter/multi-touch-canvas/</link><guid isPermaLink="true">https://rodydavis.com/flutter/multi-touch-canvas/</guid><description>A comprehensive guide and implementation for creating a multi-touch canvas in Flutter capable of panning, zooming, and object selection using advanced gesture handling.</description><pubDate>Mon, 20 Jan 2025 03:07:56 GMT</pubDate><content:encoded>&lt;h1&gt;Multi-touch Canvas with Flutter&lt;/h1&gt;
&lt;p&gt;If you ever wanted to create a canvas in &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; that needs to be panned in any direction and allow zoom then you also probably tried to create a &lt;a href=&quot;https://api.flutter.dev/flutter/gestures/MultiDragGestureRecognizer-class.html&quot;&gt;MultiGestureRecognizer&lt;/a&gt; or under a &lt;a href=&quot;https://api.flutter.dev/flutter/widgets/GestureDetector-class.html&quot;&gt;GestureDetector&lt;/a&gt; added onPanUpdate and onScaleUpdate and received an error because both can not work at the same time. Even if you have to GestureDetectors then you will still find it does not work how you want and one will always win.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/flutter_multi_touch_canvas&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/flutter_multi_touch_canvas/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the canvas rendering logic used in &lt;a href=&quot;https://widget.studio/&quot;&gt;https://widget.studio&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Multi Touch Goal&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Pan the canvas with two or more fingers&lt;/li&gt;
&lt;li&gt;Zoom the canvas with two fingers only (Pinch/Zoom)&lt;/li&gt;
&lt;li&gt;Single finger will interact with canvas object and detect selection&lt;/li&gt;
&lt;li&gt;Bonus trackpad support with similar results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to achieve this we need to use a Listener for the trackpad events and raw touch interactions and &lt;a href=&quot;https://api.flutter.dev/flutter/widgets/RawKeyboardListener-class.html&quot;&gt;RawKeyboardListener&lt;/a&gt; for keyboard shortcuts.&lt;/p&gt;
&lt;h2&gt;Part 1 - Project Setup&lt;/h2&gt;
&lt;p&gt;Open your terminal and type the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir flutter_multi_touch
cd flutter_multi_touch
flutter create .
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last line is optional and if you have VSCode installed. The command will open the directory inside VSCode.&lt;/p&gt;
&lt;h2&gt;Part 2 - Boilerplate&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Remove all comments&lt;/li&gt;
&lt;li&gt;Remove extra empty lines&lt;/li&gt;
&lt;li&gt;Update UI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Right now when you run the project you will have this UI.&lt;/p&gt;
&lt;p&gt;Create a new file located at &lt;code&gt;ui/home/screen.dart&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update &lt;code&gt;main.dart&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;ui/home/screen.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      darkTheme: ThemeData.dark().copyWith(
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomeScreen(),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will now have a black screen when you run the application.&lt;/p&gt;
&lt;h2&gt;Part 3 - Creating the Controller&lt;/h2&gt;
&lt;p&gt;Now we want to create a class that will act as our controller on the canvas.&lt;br&gt;
Create a new file at &lt;code&gt;src/controllers/canvas.dart&lt;/code&gt; and add the following to start:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;

/// Control the canvas and the objects on it
class CanvasController {
  // Controller for the stream output
  final _controller = StreamController&amp;lt;CanvasController&amp;gt;();
  // Reference to the stream to update the UI
  Stream&amp;lt;CanvasController&amp;gt; get stream =&amp;gt; _controller.stream;
  // Emit a new event to rebuild the UI
  void add([CanvasController val]) =&amp;gt; _controller.add(val ?? this);
  // Stop the stream and finish
  void close() =&amp;gt; _controller.close();
	// Start the stream
  void init() =&amp;gt; add();
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the home screen with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;../../src/controllers/canvas.dart&apos;;

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key key}) : super(key: key);

  @override
  _HomeScreenState createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  final _controller = CanvasController();

  @override
  void initState() {
    _controller.init();
    super.initState();
  }

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder&amp;lt;CanvasController&amp;gt;(
        stream: _controller.stream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Scaffold(
              appBar: AppBar(),
              body: Center(child: CircularProgressIndicator()),
            );
          }
          final instance = snapshot.data;
          return Scaffold(
            appBar: AppBar(),
            body: Stack(
              children: [
                Positioned(
                  top: 20,
                  left: 20,
                  width: 100,
                  height: 100,
                  child: Container(color: Colors.red),
                )
              ],
            ),
          );
        });
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are just adding the basics to rebuild when the controller changes or the screen is finished. We are using a stateful widget here because we want to dispose of the controller and load it only once. We are also using a stack because thats all we need under the hood. After a quick hot restart you should have the following view.&lt;/p&gt;
&lt;h2&gt;Part 4 - Adding Canvas Objects&lt;/h2&gt;
&lt;p&gt;Now we need to create the class for the objects that will be stored on the canvas. Create a new file at &lt;code&gt;src/classes/canvas_object.dart&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:ui&apos;;

class CanvasObject&amp;lt;T&amp;gt; {
  final double dx;
  final double dy;
  final double width;
  final double height;
  final T child;

  CanvasObject({
    this.dx = 0,
    this.dy = 0,
    this.width = 100,
    this.height = 100,
    this.child,
  });

  CanvasObject&amp;lt;T&amp;gt; copyWith({
    double dx,
    double dy,
    double width,
    double height,
    T child,
  }) {
    return CanvasObject&amp;lt;T&amp;gt;(
      dx: dx ?? this.dx,
      dy: dy ?? this.dy,
      width: width ?? this.width,
      height: height ?? this.height,
      child: child ?? this.child,
    );
  }

  Size get size =&amp;gt; Size(width, height);
  Offset get offset =&amp;gt; Offset(dx, dy);
  Rect get rect =&amp;gt; offset &amp;amp; size;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are using a generic here to not depend on flutter or material in the class. Update the controller with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;

import &apos;package:flutter/material.dart&apos;;

import &apos;../classes/canvas_object.dart&apos;;

/// Control the canvas and the objects on it
class CanvasController {
  /// Controller for the stream output
  final _controller = StreamController&amp;lt;CanvasController&amp;gt;();

  /// Reference to the stream to update the UI
  Stream&amp;lt;CanvasController&amp;gt; get stream =&amp;gt; _controller.stream;

  /// Emit a new event to rebuild the UI
  void add([CanvasController val]) =&amp;gt; _controller.add(val ?? this);

  /// Stop the stream and finish
  void close() =&amp;gt; _controller.close();

  /// Start the stream
  void init() =&amp;gt; add();

  // -- Canvas Objects --

  final List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; _objects = [];

  /// Current Objects on the canvas
  List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; get objects =&amp;gt; _objects;

  /// Add an object to the canvas
  void addObject(CanvasObject&amp;lt;Widget&amp;gt; value) =&amp;gt; _update(() {
        _objects.add(value);
      });

  /// Add an object to the canvas
  void updateObject(int i, CanvasObject&amp;lt;Widget&amp;gt; value) =&amp;gt; _update(() {
        _objects[i] = value;
      });

  /// Remove an object from the canvas
  void removeObject(int i) =&amp;gt; _update(() {
        _objects.removeAt(i);
      });

  void _update(void Function() action) {
    action();
    add(this);
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are just adding the objects to the canvas and removing them if needed. Update the home screen with the following to use these new objects:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;../../src/classes/canvas_object.dart&apos;;
import &apos;../../src/controllers/canvas.dart&apos;;

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key key}) : super(key: key);

  @override
  _HomeScreenState createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  final _controller = CanvasController();

  @override
  void initState() {
    _controller.init();
    _dummyData();
    super.initState();
  }

  void _dummyData() {
    _controller.addObject(
      CanvasObject(
        dx: 20,
        dy: 20,
        width: 100,
        height: 100,
        child: Container(color: Colors.red),
      ),
    );
  }

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder&amp;lt;CanvasController&amp;gt;(
        stream: _controller.stream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Scaffold(
              appBar: AppBar(),
              body: Center(child: CircularProgressIndicator()),
            );
          }
          final instance = snapshot.data;
          return Scaffold(
            appBar: AppBar(),
            body: Stack(
              children: [
                for (final object in instance.objects)
                  Positioned(
                    top: object.dy,
                    left: object.dx,
                    width: object.width,
                    height: object.height,
                    child: object.child,
                  )
              ],
            ),
          );
        });
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The UI is thee same as before but now is dynamic and we have access to the Stack children and position of each child.&lt;/p&gt;
&lt;h2&gt;Part 5 - Capture the Input&lt;/h2&gt;
&lt;p&gt;We need to capture the input of the MultiGestureRecognizer, GestureDetector and RawKeyboardListener. Update the canvas controller with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;

import &apos;package:flutter/material.dart&apos;;

import &apos;../classes/canvas_object.dart&apos;;

/// Control the canvas and the objects on it
class CanvasController {
  /// Controller for the stream output
  final _controller = StreamController&amp;lt;CanvasController&amp;gt;();

  /// Reference to the stream to update the UI
  Stream&amp;lt;CanvasController&amp;gt; get stream =&amp;gt; _controller.stream;

  /// Emit a new event to rebuild the UI
  void add([CanvasController val]) =&amp;gt; _controller.add(val ?? this);

  /// Stop the stream and finish
  void close() {
    _controller.close();
    focusNode.dispose();
  }

  /// Start the stream
  void init() =&amp;gt; add();

  // -- Canvas Objects --

  final List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; _objects = [];

  /// Current Objects on the canvas
  List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; get objects =&amp;gt; _objects;

  /// Add an object to the canvas
  void addObject(CanvasObject&amp;lt;Widget&amp;gt; value) =&amp;gt; _update(() {
        _objects.add(value);
      });

  /// Add an object to the canvas
  void updateObject(int i, CanvasObject&amp;lt;Widget&amp;gt; value) =&amp;gt; _update(() {
        _objects[i] = value;
      });

  /// Remove an object from the canvas
  void removeObject(int i) =&amp;gt; _update(() {
        _objects.removeAt(i);
      });

  /// Focus node for listening for keyboard shortcuts
  final focusNode = FocusNode();

  /// Raw events from keys pressed
  void rawKeyEvent(BuildContext context, RawKeyEvent key) {}

  /// Called every time a new finger touches the screen
  void addTouch(int pointer, Offset offsetVal, Offset globalVal) {}

  /// Called when any of the fingers update position
  void updateTouch(int pointer, Offset offsetVal, Offset globalVal) {}

  /// Called when a finger is removed from the screen
  void removeTouch(int pointer) {}

  /// Checks if the shift key on the keyboard is pressed
  bool shiftPressed = false;

  /// Scale of the canvas
  double get scale =&amp;gt; _scale;
  double _scale = 1;
  set scale(double value) =&amp;gt; _update(() {
        _scale = value;
      });

  /// Max possible scale
  static const double maxScale = 3.0;
  /// Min possible scale
  static const double minScale = 0.2;
  /// How much to scale the canvas in increments
  static const double scaleAdjust = 0.05;

  /// Current offset of the canvas
  Offset get offset =&amp;gt; _offset;
  Offset _offset = Offset.zero;
  set offset(Offset value) =&amp;gt; _update(() {
        _offset = value;
      });

  void _update(void Function() action) {
    action();
    add(this);
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the home screen with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/gestures.dart&apos;;
import &apos;package:flutter/material.dart&apos;;

import &apos;../../src/classes/canvas_object.dart&apos;;
import &apos;../../src/controllers/canvas.dart&apos;;

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key key}) : super(key: key);

  @override
  _HomeScreenState createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  final _controller = CanvasController();

  @override
  void initState() {
    _controller.init();
    _dummyData();
    super.initState();
  }

  void _dummyData() {
    _controller.addObject(
      CanvasObject(
        dx: 20,
        dy: 20,
        width: 100,
        height: 100,
        child: Container(color: Colors.red),
      ),
    );
  }

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder&amp;lt;CanvasController&amp;gt;(
        stream: _controller.stream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Scaffold(
              appBar: AppBar(),
              body: Center(child: CircularProgressIndicator()),
            );
          }
          final instance = snapshot.data;
          return Scaffold(
            appBar: AppBar(),
            body: Listener(
              behavior: HitTestBehavior.opaque,
              onPointerSignal: (details) {
                if (details is PointerScrollEvent) {
                  GestureBinding.instance.pointerSignalResolver
                      .register(details, (event) {
                    if (event is PointerScrollEvent) {
                      if (_controller.shiftPressed) {
                        double zoomDelta = (-event.scrollDelta.dy / 300);
                        _controller.scale = _controller.scale + zoomDelta;
                      } else {
                        _controller.offset =
                            _controller.offset - event.scrollDelta;
                      }
                    }
                  });
                }
              },
              onPointerMove: (details) {
                _controller.updateTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerDown: (details) {
                _controller.addTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerUp: (details) {
                _controller.removeTouch(details.pointer);
              },
              onPointerCancel: (details) {
                _controller.removeTouch(details.pointer);
              },
              child: RawKeyboardListener(
                autofocus: true,
                focusNode: _controller.focusNode,
                onKey: (key) =&amp;gt; _controller.rawKeyEvent(context, key),
                child: Stack(
                  children: [
                    for (final object in instance.objects)
                      Positioned(
                        top: object.dy,
                        left: object.dx,
                        width: object.width,
                        height: object.height,
                        child: object.child,
                      )
                  ],
                ),
              ),
            ),
          );
        });
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All we are doing now is just mapping the inputs of the UI to the actions in the controller. Feel free to look through the comments if you are curious how each one works. Running the application should still just show the red square.&lt;/p&gt;
&lt;h2&gt;Part 5 - Canvas Offset and Scale&lt;/h2&gt;
&lt;p&gt;Now we want to start moving the canvas. Let’s first tackle the offset as scale will take a different approach. Update the home screen with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/gestures.dart&apos;;
import &apos;package:flutter/material.dart&apos;;

import &apos;../../src/classes/canvas_object.dart&apos;;
import &apos;../../src/controllers/canvas.dart&apos;;

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key key}) : super(key: key);

  @override
  _HomeScreenState createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  final _controller = CanvasController();

  @override
  void initState() {
    _controller.init();
    _dummyData();
    super.initState();
  }

  void _dummyData() {
    _controller.addObject(
      CanvasObject(
        dx: 20,
        dy: 20,
        width: 100,
        height: 100,
        child: Container(color: Colors.red),
      ),
    );
  }

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder&amp;lt;CanvasController&amp;gt;(
        stream: _controller.stream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Scaffold(
              appBar: AppBar(),
              body: Center(child: CircularProgressIndicator()),
            );
          }
          final instance = snapshot.data;
          return Scaffold(
            appBar: AppBar(),
            body: Listener(
              behavior: HitTestBehavior.opaque,
              onPointerSignal: (details) {
                if (details is PointerScrollEvent) {
                  GestureBinding.instance.pointerSignalResolver
                      .register(details, (event) {
                    if (event is PointerScrollEvent) {
                      if (_controller.shiftPressed) {
                        double zoomDelta = (-event.scrollDelta.dy / 300);
                        _controller.scale = _controller.scale + zoomDelta;
                      } else {
                        _controller.offset =
                            _controller.offset - event.scrollDelta;
                      }
                    }
                  });
                }
              },
              onPointerMove: (details) {
                _controller.updateTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerDown: (details) {
                _controller.addTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerUp: (details) {
                _controller.removeTouch(details.pointer);
              },
              onPointerCancel: (details) {
                _controller.removeTouch(details.pointer);
              },
              child: RawKeyboardListener(
                autofocus: true,
                focusNode: _controller.focusNode,
                onKey: (key) =&amp;gt; _controller.rawKeyEvent(context, key),
                child: SizedBox.expand(
                  child: Stack(
                    children: [
                      for (final object in instance.objects)
                        AnimatedPositioned.fromRect(
                          duration: const Duration(milliseconds: 50),
                          rect: object.rect.adjusted(
                            _controller.offset,
                            _controller.scale,
                          ),
                          child: FittedBox(
                            fit: BoxFit.fill,
                            child: SizedBox.fromSize(
                              size: object.size,
                              child: object.child,
                            ),
                          ),
                        )
                    ],
                  ),
                ),
              ),
            ),
          );
        });
  }
}

extension RectUtils on Rect {
  Rect adjusted(Offset offset, double scale) {
    final left = (this.left + offset.dx) * scale;
    final top = (this.top + offset.dy) * scale;
    final width = this.width * scale;
    final height = this.height * scale;
    return Rect.fromLTWH(left, top, width, height);
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you use your trackpad to pan with two fingers you will see the red square move. We now need to add finger support too. You may notice the FittedBox and that will come in as soon as we add scaling.&lt;/p&gt;
&lt;p&gt;Now if we move the square off the screen we may need to bring it back. We can add a reset button to the AppBar. Add the following to the canvas controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  static const double _scaleDefault = 1;
  static const Offset _offsetDefault = Offset.zero;

  void reset() {
    scale = _scaleDefault;
    offset = _offsetDefault;
  }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the home screen with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/gestures.dart&apos;;
import &apos;package:flutter/material.dart&apos;;

import &apos;../../src/classes/canvas_object.dart&apos;;
import &apos;../../src/controllers/canvas.dart&apos;;

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key key}) : super(key: key);

  @override
  _HomeScreenState createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  final _controller = CanvasController();

  @override
  void initState() {
    _controller.init();
    _dummyData();
    super.initState();
  }

  void _dummyData() {
    _controller.addObject(
      CanvasObject(
        dx: 20,
        dy: 20,
        width: 100,
        height: 100,
        child: Container(color: Colors.red),
      ),
    );
  }

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder&amp;lt;CanvasController&amp;gt;(
        stream: _controller.stream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Scaffold(
              appBar: AppBar(),
              body: Center(child: CircularProgressIndicator()),
            );
          }
          final instance = snapshot.data;
          return Scaffold(
            appBar: AppBar(
              actions: [
                IconButton(
                  tooltip: &apos;Reset the Scale and Offset&apos;,
                  icon: Icon(Icons.restore),
                  onPressed: _controller.reset,
                ),
              ],
            ),
            body: Listener(
              behavior: HitTestBehavior.opaque,
              onPointerSignal: (details) {
                if (details is PointerScrollEvent) {
                  GestureBinding.instance.pointerSignalResolver
                      .register(details, (event) {
                    if (event is PointerScrollEvent) {
                      if (_controller.shiftPressed) {
                        double zoomDelta = (-event.scrollDelta.dy / 300);
                        _controller.scale = _controller.scale + zoomDelta;
                      } else {
                        _controller.offset =
                            _controller.offset - event.scrollDelta;
                      }
                    }
                  });
                }
              },
              onPointerMove: (details) {
                _controller.updateTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerDown: (details) {
                _controller.addTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerUp: (details) {
                _controller.removeTouch(details.pointer);
              },
              onPointerCancel: (details) {
                _controller.removeTouch(details.pointer);
              },
              child: RawKeyboardListener(
                autofocus: true,
                focusNode: _controller.focusNode,
                onKey: (key) =&amp;gt; _controller.rawKeyEvent(context, key),
                child: SizedBox.expand(
                  child: Stack(
                    children: [
                      for (final object in instance.objects)
                        AnimatedPositioned.fromRect(
                          duration: const Duration(milliseconds: 50),
                          rect: object.rect.adjusted(
                            _controller.offset,
                            _controller.scale,
                          ),
                          child: FittedBox(
                            fit: BoxFit.fill,
                            child: SizedBox.fromSize(
                              size: object.size,
                              child: object.child,
                            ),
                          ),
                        )
                    ],
                  ),
                ),
              ),
            ),
          );
        });
  }
}

extension RectUtils on Rect {
  Rect adjusted(Offset offset, double scale) {
    final left = (this.left + offset.dx) * scale;
    final top = (this.top + offset.dy) * scale;
    final width = this.width * scale;
    final height = this.height * scale;
    return Rect.fromLTWH(left, top, width, height);
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you press the reset button the canvas animates back to the default offset and scale.&lt;/p&gt;
&lt;p&gt;While we are here we can add actions for zoom in/out and connect them to the controller. Add the following to the canvas controller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  void zoomIn() {
    scale += scaleAdjust;
  }

  void zoomOut() {
    scale -= scaleAdjust;
  }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following to the AppBar actions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;IconButton(
    tooltip: &apos;Zoom In&apos;,
    icon: Icon(Icons.zoom_in),
    onPressed: _controller.zoomIn,
 ),
IconButton(
    tooltip: &apos;Zoom Out&apos;,
    icon: Icon(Icons.zoom_out),
    onPressed: _controller.zoomOut,
),

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you run the application you can easily zoom in/out.&lt;/p&gt;
&lt;h2&gt;Part 6 - Keyboard Shortcuts&lt;/h2&gt;
&lt;p&gt;Now we need to capture the keyboard events so we can move the canvas with the arrow keys and scale with +/- keys. Update the controller with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;

import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter/services.dart&apos;;

import &apos;../classes/canvas_object.dart&apos;;

/// Control the canvas and the objects on it
class CanvasController {
  /// Controller for the stream output
  final _controller = StreamController&amp;lt;CanvasController&amp;gt;();

  /// Reference to the stream to update the UI
  Stream&amp;lt;CanvasController&amp;gt; get stream =&amp;gt; _controller.stream;

  /// Emit a new event to rebuild the UI
  void add([CanvasController val]) =&amp;gt; _controller.add(val ?? this);

  /// Stop the stream and finish
  void close() {
    _controller.close();
    focusNode.dispose();
  }

  /// Start the stream
  void init() =&amp;gt; add();

  // -- Canvas Objects --

  final List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; _objects = [];

  /// Current Objects on the canvas
  List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; get objects =&amp;gt; _objects;

  /// Add an object to the canvas
  void addObject(CanvasObject&amp;lt;Widget&amp;gt; value) =&amp;gt; _update(() {
        _objects.add(value);
      });

  /// Add an object to the canvas
  void updateObject(int i, CanvasObject&amp;lt;Widget&amp;gt; value) =&amp;gt; _update(() {
        _objects[i] = value;
      });

  /// Remove an object from the canvas
  void removeObject(int i) =&amp;gt; _update(() {
        _objects.removeAt(i);
      });

  /// Focus node for listening for keyboard shortcuts
  final focusNode = FocusNode();

  /// Raw events from keys pressed
  void rawKeyEvent(BuildContext context, RawKeyEvent key) {
    // Scale keys
    if (key.isKeyPressed(LogicalKeyboardKey.minus)) {
      zoomOut();
    }
    if (key.isKeyPressed(LogicalKeyboardKey.equal)) {
      zoomIn();
    }
    // Directional Keys
    if (key.isKeyPressed(LogicalKeyboardKey.arrowLeft)) {
      offset = offset + Offset(offsetAdjust, 0.0);
    }
    if (key.isKeyPressed(LogicalKeyboardKey.arrowRight)) {
      offset = offset + Offset(-offsetAdjust, 0.0);
    }
    if (key.isKeyPressed(LogicalKeyboardKey.arrowUp)) {
      offset = offset + Offset(0.0, offsetAdjust);
    }
    if (key.isKeyPressed(LogicalKeyboardKey.arrowDown)) {
      offset = offset + Offset(0.0, -offsetAdjust);
    }

    _shiftPressed = key.isShiftPressed;

    /// Update Controller Instance
    add(this);
  }

  /// Called every time a new finger touches the screen
  void addTouch(int pointer, Offset offsetVal, Offset globalVal) {}

  /// Called when any of the fingers update position
  void updateTouch(int pointer, Offset offsetVal, Offset globalVal) {}

  /// Called when a finger is removed from the screen
  void removeTouch(int pointer) {}

  /// Checks if the shift key on the keyboard is pressed
  bool get shiftPressed =&amp;gt; _shiftPressed;
  bool _shiftPressed = false;

  /// Scale of the canvas
  double get scale =&amp;gt; _scale;
  double _scale = 1;
  set scale(double value) =&amp;gt; _update(() {
        _scale = value;
      });

  /// Max possible scale
  static const double maxScale = 3.0;

  /// Min possible scale
  static const double minScale = 0.2;

  /// How much to scale the canvas in increments
  static const double scaleAdjust = 0.05;

  /// How much to shift the canvas in increments
  static const double offsetAdjust = 15;

  /// Current offset of the canvas
  Offset get offset =&amp;gt; _offset;
  Offset _offset = Offset.zero;
  set offset(Offset value) =&amp;gt; _update(() {
        _offset = value;
      });

  static const double _scaleDefault = 1;
  static const Offset _offsetDefault = Offset.zero;

  /// Reset the canvas zoom and offset
  void reset() {
    scale = _scaleDefault;
    offset = _offsetDefault;
  }

  /// Zoom in the canvas
  void zoomIn() {
    scale += scaleAdjust;
  }

  /// Zoom out the canvas
  void zoomOut() {
    scale -= scaleAdjust;
  }

  void _update(void Function() action) {
    action();
    add(this);
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you run the application you can control the zoom and pan with just a keyboard. This could be useful for a fallback input that would work on a TV for example…&lt;/p&gt;
&lt;p&gt;If you want to see if it is actually scaling proportionally then add the following the home screen:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/gestures.dart&apos;;
import &apos;package:flutter/material.dart&apos;;

import &apos;../../src/classes/canvas_object.dart&apos;;
import &apos;../../src/controllers/canvas.dart&apos;;

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key key}) : super(key: key);

  @override
  _HomeScreenState createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  final _controller = CanvasController();

  @override
  void initState() {
    _controller.init();
    _dummyData();
    super.initState();
  }

  void _dummyData() {
    _controller.addObject(
      CanvasObject(
        dx: 20,
        dy: 20,
        width: 100,
        height: 100,
        child: Container(color: Colors.red),
      ),
    );
    _controller.addObject(
      CanvasObject(
        dx: 80,
        dy: 60,
        width: 100,
        height: 200,
        child: Container(color: Colors.green),
      ),
    );
    _controller.addObject(
      CanvasObject(
        dx: 100,
        dy: 40,
        width: 100,
        height: 50,
        child: Container(color: Colors.blue),
      ),
    );
  }

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder&amp;lt;CanvasController&amp;gt;(
        stream: _controller.stream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Scaffold(
              appBar: AppBar(),
              body: Center(child: CircularProgressIndicator()),
            );
          }
          final instance = snapshot.data;
          return Scaffold(
            appBar: AppBar(
              actions: [
                FocusScope(
                  canRequestFocus: false,
                  child: IconButton(
                    tooltip: &apos;Zoom In&apos;,
                    icon: Icon(Icons.zoom_in),
                    onPressed: _controller.zoomIn,
                  ),
                ),
                FocusScope(
                  canRequestFocus: false,
                  child: IconButton(
                    tooltip: &apos;Zoom Out&apos;,
                    icon: Icon(Icons.zoom_out),
                    onPressed: _controller.zoomOut,
                  ),
                ),
                FocusScope(
                  canRequestFocus: false,
                  child: IconButton(
                    tooltip: &apos;Reset the Scale and Offset&apos;,
                    icon: Icon(Icons.restore),
                    onPressed: _controller.reset,
                  ),
                ),
              ],
            ),
            body: Listener(
              behavior: HitTestBehavior.opaque,
              onPointerSignal: (details) {
                if (details is PointerScrollEvent) {
                  GestureBinding.instance.pointerSignalResolver
                      .register(details, (event) {
                    if (event is PointerScrollEvent) {
                      if (_controller.shiftPressed) {
                        double zoomDelta = (-event.scrollDelta.dy / 300);
                        _controller.scale = _controller.scale + zoomDelta;
                      } else {
                        _controller.offset =
                            _controller.offset - event.scrollDelta;
                      }
                    }
                  });
                }
              },
              onPointerMove: (details) {
                _controller.updateTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerDown: (details) {
                _controller.addTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerUp: (details) {
                _controller.removeTouch(details.pointer);
              },
              onPointerCancel: (details) {
                _controller.removeTouch(details.pointer);
              },
              child: RawKeyboardListener(
                autofocus: true,
                focusNode: _controller.focusNode,
                onKey: (key) =&amp;gt; _controller.rawKeyEvent(context, key),
                child: SizedBox.expand(
                  child: Stack(
                    children: [
                      for (final object in instance.objects)
                        AnimatedPositioned.fromRect(
                          duration: const Duration(milliseconds: 50),
                          rect: object.rect.adjusted(
                            _controller.offset,
                            _controller.scale,
                          ),
                          child: FittedBox(
                            fit: BoxFit.fill,
                            child: SizedBox.fromSize(
                              size: object.size,
                              child: object.child,
                            ),
                          ),
                        )
                    ],
                  ),
                ),
              ),
            ),
          );
        });
  }
}

extension RectUtils on Rect {
  Rect adjusted(Offset offset, double scale) {
    final left = (this.left + offset.dx) * scale;
    final top = (this.top + offset.dy) * scale;
    final width = this.width * scale;
    final height = this.height * scale;
    return Rect.fromLTWH(left, top, width, height);
  }
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can zoom and the blocks all scale correctly and pan around.&lt;/p&gt;
&lt;p&gt;Just press the reset button to start over.&lt;/p&gt;
&lt;h2&gt;Part 7 - Multi Touch Input&lt;/h2&gt;
&lt;p&gt;Now time for the fingers. For this you will need a touchscreen device to test. You can plug in your phone or if you have a touch screen computer you can run the web version. Update the controller with following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;
import &apos;dart:math&apos; as math;

import &apos;package:flutter/gestures.dart&apos;;
import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter/services.dart&apos;;

import &apos;../classes/canvas_object.dart&apos;;
import &apos;../classes/rect_points.dart&apos;;

/// Control the canvas and the objects on it
class CanvasController {
  /// Controller for the stream output
  final _controller = StreamController&amp;lt;CanvasController&amp;gt;();

  /// Reference to the stream to update the UI
  Stream&amp;lt;CanvasController&amp;gt; get stream =&amp;gt; _controller.stream;

  /// Emit a new event to rebuild the UI
  void add([CanvasController val]) =&amp;gt; _controller.add(val ?? this);

  /// Stop the stream and finish
  void close() {
    _controller.close();
    focusNode.dispose();
  }

  /// Start the stream
  void init() =&amp;gt; add();

  // -- Canvas Objects --

  final List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; _objects = [];

  /// Current Objects on the canvas
  List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; get objects =&amp;gt; _objects;

  /// Add an object to the canvas
  void addObject(CanvasObject&amp;lt;Widget&amp;gt; value) =&amp;gt; _update(() {
        _objects.add(value);
      });

  /// Add an object to the canvas
  void updateObject(int i, CanvasObject&amp;lt;Widget&amp;gt; value) =&amp;gt; _update(() {
        _objects[i] = value;
      });

  /// Remove an object from the canvas
  void removeObject(int i) =&amp;gt; _update(() {
        _objects.removeAt(i);
      });

  /// Focus node for listening for keyboard shortcuts
  final focusNode = FocusNode();

  /// Raw events from keys pressed
  void rawKeyEvent(BuildContext context, RawKeyEvent key) {
    // Scale keys
    if (key.isKeyPressed(LogicalKeyboardKey.minus)) {
      zoomOut();
    }
    if (key.isKeyPressed(LogicalKeyboardKey.equal)) {
      zoomIn();
    }
    // Directional Keys
    if (key.isKeyPressed(LogicalKeyboardKey.arrowLeft)) {
      offset = offset + Offset(offsetAdjust, 0.0);
    }
    if (key.isKeyPressed(LogicalKeyboardKey.arrowRight)) {
      offset = offset + Offset(-offsetAdjust, 0.0);
    }
    if (key.isKeyPressed(LogicalKeyboardKey.arrowUp)) {
      offset = offset + Offset(0.0, offsetAdjust);
    }
    if (key.isKeyPressed(LogicalKeyboardKey.arrowDown)) {
      offset = offset + Offset(0.0, -offsetAdjust);
    }

    _shiftPressed = key.isShiftPressed;
    _metaPressed = key.isMetaPressed;

    /// Update Controller Instance
    add(this);
  }

  /// Trigger Shift Press
  void shiftSelect() {
    _shiftPressed = true;
  }

  /// Trigger Meta Press
  void metaSelect() {
    _metaPressed = true;
  }

  final Map&amp;lt;int, Offset&amp;gt; _pointerMap = {};

  /// Number of inputs currently on the screen
  int get touchCount =&amp;gt; _pointerMap.values.length;

  /// Marquee selection on the canvas
  RectPoints get marquee =&amp;gt; _marquee;
  RectPoints _marquee;

  /// Dragging a canvas object
  bool get isMovingCanvasObject =&amp;gt; _isMovingCanvasObject;
  bool _isMovingCanvasObject = false;

  final List&amp;lt;int&amp;gt; _selectedObjects = [];
  List&amp;lt;int&amp;gt; get selectedObjectsIndices =&amp;gt; _selectedObjects;
  List&amp;lt;CanvasObject&amp;lt;Widget&amp;gt;&amp;gt; get selectedObjects =&amp;gt;
      _selectedObjects.map((i) =&amp;gt; _objects[i]).toList();
  bool isObjectSelected(int i) =&amp;gt; _selectedObjects.contains(i);

  /// Called every time a new input touches the screen
  void addTouch(int pointer, Offset offsetVal, Offset globalVal) {
    _pointerMap[pointer] = offsetVal;

    if (shiftPressed) {
      final pt = (offsetVal / scale) - (offset);
      _marquee = RectPoints(pt, pt);
    }

    /// Update Controller Instance
    add(this);
  }

  /// Called when any of the inputs update position
  void updateTouch(int pointer, Offset offsetVal, Offset globalVal) {
    if (_marquee != null) {
      // Update New Widget Rect
      final _pts = _marquee;
      final a = _pointerMap.values.first;
      _pointerMap[pointer] = offsetVal;
      final b = _pointerMap.values.first;
      final delta = (b - a) / scale;
      _pts.end = _pts.end + delta;
      _marquee = _pts;
      final _rect = Rect.fromPoints(_pts.start, _pts.end);
      _selectedObjects.clear();
      for (var i = 0; i &amp;lt; _objects.length; i++) {
        if (_rect.overlaps(_objects[i].rect)) {
          _selectedObjects.add(i);
        }
      }
    } else if (touchCount == 1) {
      // Widget Move
      _isMovingCanvasObject = true;
      final a = _pointerMap.values.first;
      _pointerMap[pointer] = offsetVal;
      final b = _pointerMap.values.first;
      if (_selectedObjects.isEmpty) return;
      for (final idx in _selectedObjects) {
        final widget = _objects[idx];
        final delta = (b - a) / scale;
        final _newOffset = widget.offset + delta;
        _objects[idx] = widget.copyWith(dx: _newOffset.dx, dy: _newOffset.dy);
      }
    } else if (touchCount == 2) {
      // Scale and Rotate Update
      _isMovingCanvasObject = false;
      final _rectA = _getRectFromPoints(_pointerMap.values.toList());
      _pointerMap[pointer] = offsetVal;
      final _rectB = _getRectFromPoints(_pointerMap.values.toList());
      final _delta = _rectB.center - _rectA.center;
      final _newOffset = offset + (_delta / scale);
      offset = _newOffset;
      final aDistance = (_rectA.topLeft - _rectA.bottomRight).distance;
      final bDistance = (_rectB.topLeft - _rectB.bottomRight).distance;
      final change = (bDistance / aDistance);
      scale = scale * change;
    } else {
      // Pan Update
      _isMovingCanvasObject = false;
      final _rectA = _getRectFromPoints(_pointerMap.values.toList());
      _pointerMap[pointer] = offsetVal;
      final _rectB = _getRectFromPoints(_pointerMap.values.toList());
      final _delta = _rectB.center - _rectA.center;
      offset = offset + (_delta / scale);
    }
    _pointerMap[pointer] = offsetVal;

    /// Update Controller Instance
    add(this);
  }

  /// Called when a input is removed from the screen
  void removeTouch(int pointer) {
    _pointerMap.remove(pointer);

    if (touchCount &amp;lt; 1) {
      _isMovingCanvasObject = false;
    }
    if (_marquee != null) {
      _marquee = null;
      _shiftPressed = false;
    }

    /// Update Controller Instance
    add(this);
  }

  void selectObject(int i) =&amp;gt; _update(() {
        if (!_metaPressed) {
          _selectedObjects.clear();
        }
        _selectedObjects.add(0);
        final item = _objects.removeAt(i);
        _objects.insert(0, item);
      });

  /// Checks if the shift key on the keyboard is pressed
  bool get shiftPressed =&amp;gt; _shiftPressed;
  bool _shiftPressed = false;

  /// Checks if the meta key on the keyboard is pressed
  bool get metaPressed =&amp;gt; _metaPressed;
  bool _metaPressed = false;

  /// Scale of the canvas
  double get scale =&amp;gt; _scale;
  double _scale = 1;
  set scale(double value) =&amp;gt; _update(() {
        if (value &amp;lt;= minScale) {
          value = minScale;
        } else if (value &amp;gt;= maxScale) {
          value = maxScale;
        }
        _scale = value;
      });

  /// Max possible scale
  static const double maxScale = 3.0;

  /// Min possible scale
  static const double minScale = 0.2;

  /// How much to scale the canvas in increments
  static const double scaleAdjust = 0.05;

  /// How much to shift the canvas in increments
  static const double offsetAdjust = 15;

  /// Current offset of the canvas
  Offset get offset =&amp;gt; _offset;
  Offset _offset = Offset.zero;
  set offset(Offset value) =&amp;gt; _update(() {
        _offset = value;
      });

  static const double _scaleDefault = 1;
  static const Offset _offsetDefault = Offset.zero;

  /// Reset the canvas zoom and offset
  void reset() {
    scale = _scaleDefault;
    offset = _offsetDefault;
  }

  /// Zoom in the canvas
  void zoomIn() {
    scale += scaleAdjust;
  }

  /// Zoom out the canvas
  void zoomOut() {
    scale -= scaleAdjust;
  }

  void _update(void Function() action) {
    action();
    add(this);
  }

  Rect _getRectFromPoints(List&amp;lt;Offset&amp;gt; offsets) {
    if (offsets.length == 2) {
      return Rect.fromPoints(offsets.first, offsets.last);
    }
    final dxs = offsets.map((e) =&amp;gt; e.dx).toList();
    final dys = offsets.map((e) =&amp;gt; e.dy).toList();
    double left = _minFromList(dxs);
    double top = _minFromList(dys);
    double bottom = _maxFromList(dys);
    double right = _maxFromList(dxs);
    return Rect.fromLTRB(left, top, right, bottom);
  }

  double _minFromList(List&amp;lt;double&amp;gt; values) {
    double value = double.infinity;
    for (final item in values) {
      value = math.min(item, value);
    }
    return value;
  }

  double _maxFromList(List&amp;lt;double&amp;gt; values) {
    double value = -double.infinity;
    for (final item in values) {
      value = math.max(item, value);
    }
    return value;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a new file &lt;code&gt;src/classes/rect_points.dart&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:ui&apos;;

class RectPoints {
  RectPoints(this.start, this.end);

  Offset start, end;

  Rect get rect =&amp;gt; Rect.fromPoints(start, end);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;main.dart&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;ui/home/screen.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        accentColor: Colors.red,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      darkTheme: ThemeData.dark().copyWith(
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomeScreen(),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the home screen with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/gestures.dart&apos;;
import &apos;package:flutter/material.dart&apos;;

import &apos;../../src/classes/canvas_object.dart&apos;;
import &apos;../../src/controllers/canvas.dart&apos;;

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key key}) : super(key: key);

  @override
  _HomeScreenState createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  final _controller = CanvasController();

  @override
  void initState() {
    _controller.init();
    _dummyData();
    super.initState();
  }

  void _dummyData() {
    _controller.addObject(
      CanvasObject(
        dx: 20,
        dy: 20,
        width: 100,
        height: 100,
        child: Container(color: Colors.red),
      ),
    );
    _controller.addObject(
      CanvasObject(
        dx: 80,
        dy: 60,
        width: 100,
        height: 200,
        child: Container(color: Colors.green),
      ),
    );
    _controller.addObject(
      CanvasObject(
        dx: 100,
        dy: 40,
        width: 100,
        height: 50,
        child: Container(color: Colors.blue),
      ),
    );
  }

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder&amp;lt;CanvasController&amp;gt;(
        stream: _controller.stream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Scaffold(
              appBar: AppBar(),
              body: Center(child: CircularProgressIndicator()),
            );
          }
          final instance = snapshot.data;
          return Scaffold(
            appBar: AppBar(
              actions: [
                FocusScope(
                  canRequestFocus: false,
                  child: IconButton(
                    tooltip: &apos;Selection&apos;,
                    icon: Icon(Icons.select_all),
                    color: instance.shiftPressed
                        ? Theme.of(context).accentColor
                        : null,
                    onPressed: _controller.shiftSelect,
                  ),
                ),
                FocusScope(
                  canRequestFocus: false,
                  child: IconButton(
                    tooltip: &apos;Meta Key&apos;,
                    color: instance.metaPressed
                        ? Theme.of(context).accentColor
                        : null,
                    icon: Icon(Icons.category),
                    onPressed: _controller.metaSelect,
                  ),
                ),
                FocusScope(
                  canRequestFocus: false,
                  child: IconButton(
                    tooltip: &apos;Zoom In&apos;,
                    icon: Icon(Icons.zoom_in),
                    onPressed: _controller.zoomIn,
                  ),
                ),
                FocusScope(
                  canRequestFocus: false,
                  child: IconButton(
                    tooltip: &apos;Zoom Out&apos;,
                    icon: Icon(Icons.zoom_out),
                    onPressed: _controller.zoomOut,
                  ),
                ),
                FocusScope(
                  canRequestFocus: false,
                  child: IconButton(
                    tooltip: &apos;Reset the Scale and Offset&apos;,
                    icon: Icon(Icons.restore),
                    onPressed: _controller.reset,
                  ),
                ),
              ],
            ),
            body: Listener(
              behavior: HitTestBehavior.opaque,
              onPointerSignal: (details) {
                if (details is PointerScrollEvent) {
                  GestureBinding.instance.pointerSignalResolver
                      .register(details, (event) {
                    if (event is PointerScrollEvent) {
                      if (_controller.shiftPressed) {
                        double zoomDelta = (-event.scrollDelta.dy / 300);
                        _controller.scale = _controller.scale + zoomDelta;
                      } else {
                        _controller.offset =
                            _controller.offset - event.scrollDelta;
                      }
                    }
                  });
                }
              },
              onPointerMove: (details) {
                _controller.updateTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerDown: (details) {
                _controller.addTouch(
                  details.pointer,
                  details.localPosition,
                  details.position,
                );
              },
              onPointerUp: (details) {
                _controller.removeTouch(details.pointer);
              },
              onPointerCancel: (details) {
                _controller.removeTouch(details.pointer);
              },
              child: RawKeyboardListener(
                autofocus: true,
                focusNode: _controller.focusNode,
                onKey: (key) =&amp;gt; _controller.rawKeyEvent(context, key),
                child: SizedBox.expand(
                  child: Stack(
                    children: [
                      for (var i = 0; i &amp;lt; instance.objects.length; i++)
                        Positioned.fromRect(
                          rect: instance.objects[i].rect.adjusted(
                            _controller.offset,
                            _controller.scale,
                          ),
                          child: Container(
                            decoration: BoxDecoration(
                                border: Border.all(
                              color: instance.isObjectSelected(i)
                                  ? Colors.grey
                                  : Colors.transparent,
                            )),
                            child: GestureDetector(
                              onTapDown: (_) =&amp;gt; _controller.selectObject(i),
                              child: FittedBox(
                                fit: BoxFit.fill,
                                child: SizedBox.fromSize(
                                  size: instance.objects[i].size,
                                  child: instance.objects[i].child,
                                ),
                              ),
                            ),
                          ),
                        ),
                      if (instance?.marquee != null)
                        Positioned.fromRect(
                          rect: instance.marquee.rect
                              .adjusted(instance.offset, instance.scale),
                          child: Container(
                            color: Colors.blueAccent.withOpacity(0.3),
                          ),
                        ),
                    ],
                  ),
                ),
              ),
            ),
          );
        });
  }
}

extension RectUtils on Rect {
  Rect adjusted(Offset offset, double scale) {
    final left = (this.left + offset.dx) * scale;
    final top = (this.top + offset.dy) * scale;
    final width = this.width * scale;
    final height = this.height * scale;
    return Rect.fromLTWH(left, top, width, height);
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can move any object on the canvas just by clicking and dragging. You can zoom with 2 fingers and pan with 2 or 3 fingers. If you hold down the shift key then you can use a marquee to select multiple and if you hold down the meta/command key then you can select multiple by tapping each.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you are on a device without a keyboard you can tap the new icons to turn on the keyboard key actions. When the object is selected there is a grey border.&lt;/p&gt;
&lt;p&gt;Now you can add any widget to the canvas and pan and zoom!&lt;/p&gt;
</content:encoded></item><item><title>How to build a native cross platform project with Flutter</title><link>https://rodydavis.com/flutter/native-cross-platform/</link><guid isPermaLink="true">https://rodydavis.com/flutter/native-cross-platform/</guid><description>Flutter now supports unified, native cross-platform development by allowing developers to use any plugin and seamlessly target web, iOS, Android, and desktop from a single codebase.</description><pubDate>Mon, 20 Jan 2025 00:56:31 GMT</pubDate><content:encoded>&lt;h1&gt;How to build a native cross platform project with Flutter&lt;/h1&gt;
&lt;p&gt;Import dart:html and dart:io in the same project!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/flutter_x/tree/finish&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Up to now you have been able to create projects with Flutter that run on iOS/Android, Web and Desktop but only sharing pure dart plugins.&lt;/p&gt;
&lt;p&gt;Flutter launched &lt;em&gt;Flutter for web&lt;/em&gt; at Google I/O and was a temporary fork that required you to change imports from &lt;code&gt;import &apos;package:flutter/material.dart&apos;;&lt;/code&gt; to &lt;code&gt;import &apos;package:flutter_web/material.dart&apos;;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As you can image this was really difficult for a code base as you had to create a fork and change the imports. This also meant that you could not import any package that needed on a path or depended on flutter. The time as come and the merge is complete. Now you no longer need to change the imports!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/n_2_kvxta7kwbn.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can use any plugin now, have a debugger, create new flutter projects with the web folder added, web plugins, and so much more..&lt;/p&gt;
&lt;h2&gt;Disclaimer&lt;/h2&gt;
&lt;p&gt;You will need to be on the latest flutter for this to work.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://flutter.io/get-started/install/&quot;&gt;Download Flutter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/ff_1_u67ip3pbk5.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you are pretty new to Flutter you can check out &lt;a href=&quot;https://flutter.io/get-started/codelab/&quot;&gt;this useful guide&lt;/a&gt; on how to create a new project step by step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/ff_2_9dljl4hgqq.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create a new project named &lt;strong&gt;flutter_x&lt;/strong&gt; and it should look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_1_mdevnjnwh7.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can also down the starter project &lt;a href=&quot;https://github.com/rodydavis/flutter_x/tree/starter&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Your code should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with &amp;quot;flutter run&amp;quot;. You&apos;ll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // &amp;quot;hot reload&amp;quot; (press &amp;quot;r&amp;quot; in the console where you ran &amp;quot;flutter run&amp;quot;,
        // or simply save your changes to &amp;quot;hot reload&amp;quot; in a Flutter IDE).
        // Notice that the counter didn&apos;t reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: &apos;Flutter Demo Home Page&apos;),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked &amp;quot;final&amp;quot;.

  final String title;

  @override
  _MyHomePageState createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke &amp;quot;debug painting&amp;quot; (press &amp;quot;p&amp;quot; in the console, choose the
          // &amp;quot;Toggle Debug Paint&amp;quot; action from the Flutter Inspector in Android
          // Studio, or the &amp;quot;Toggle Debug Paint&amp;quot; command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            Text(
              &apos;You have pushed the button this many times:&apos;,
            ),
            Text(
              &apos;$_counter&apos;,
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just to make sure everything is working go ahead and run the project on iOS/Android.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_2_srn7elmk0c.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You should have the counter application running and working correctly. Now quit and run on Chrome. It should be listed as a device. You can also run from the command line flutter run -d chrome.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_3_iq2vozt6xi.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You do not get hot reload yet on web so be aware of that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_4_f6ht1u443m.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Your project should now look like this.&lt;/p&gt;
&lt;p&gt;Open your pubspec.yaml and import the following packages.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;dependencies:
  universal_html:
  url_launcher:
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also remove the comments generated in the pubspec.yaml&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Your pubspec.yaml will now read like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;name: flutter_x
description: A new Flutter project.

version: 1.0.0+1

environment:
  sdk: &amp;quot;&amp;gt;=2.1.0 &amp;lt;3.0.0&amp;quot;

dependencies:
  flutter:
    sdk: flutter

cupertino_icons: ^0.1.2
  universal_html: ^1.1.0
  url_launcher: ^5.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:

uses-material-design: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default if you were to check if the device was mobile or web you will get an error at compile time when trying to import a plugin that is not meant for the platform. To get around this we will use dynamic imports.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_5_a7g3ejp2xp.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create a url_launcher folder and file url_launcher.dart, mobile.dart, web.dart, unsupported.dart inside the plugins folder.&lt;/p&gt;
&lt;p&gt;In the file url_launcher.dart add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;export &apos;unsupported.dart&apos;
    if (dart.library.html) &apos;web.dart&apos;
    if (dart.library.io) &apos;mobile.dart&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will pick the correct file at runtime and give a fallback if it is not supported.&lt;/p&gt;
&lt;p&gt;To protect against edge cases you will need to set up a fallback for the import. In unsupported.dart add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;class UrlUtils {
  UrlUtils._();

static void open(String url, {String name}) {
    throw &apos;Platform Not Supported&apos;;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The class UrlUtils and the public methods have to match all three files for this to work correctly. Always set up the unsupported first then copy the file into mobile.dart and web.dart to ensure no typos.&lt;/p&gt;
&lt;p&gt;You should now have 3 files with the above code in each class.&lt;/p&gt;
&lt;p&gt;In mobile.dart add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:url_launcher/url_launcher.dart&apos;;

class UrlUtils {
  UrlUtils._();

static void open(String url, {String name}) async {
    if (await canLaunch(url)) {
      await launch(url);
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will open the link in safari view controller or android’s default browser respectively.&lt;/p&gt;
&lt;p&gt;In web.dart add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:universal_html/prefer_universal/html.dart&apos; as html;

class UrlUtils {
  UrlUtils._();

static void open(String url, {String name}) {
    html.window.open(url, name);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will open up a new window in the browser with the specified link.&lt;/p&gt;
&lt;p&gt;Add a button to the center of the screen. The ui/home/screen.dart should read the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override
  _MyHomePageState createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: RaisedButton(
        child: Text(&apos;Open Flutter.dev&apos;),
        onPressed: () {},
      )),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the onPressed to the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;onPressed: () {
    try {
        UrlUtils.open(&apos;[https://flutter.dev&apos;](https://flutter.dev&apos;));
    } catch (e) {
        print(&apos;Error -&amp;gt; $e&apos;);
    }
},
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you go to import the UrlUtils it is important to import the correct URI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_6_z13qycty3g.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Make sure to import &lt;code&gt;import &apos;package:flutter_x/plugins/url_launcher/url_launcher.dart&apos;;&lt;/code&gt; only.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can use the relative import if you wish.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You UI code will now read the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;../../plugins/url_launcher/url_launcher.dart&apos;;

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override
  _MyHomePageState createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: RaisedButton(
        child: Text(&apos;Open Flutter.dev&apos;),
        onPressed: () {
          try {
            UrlUtils.open(&apos;[https://flutter.dev&apos;](https://flutter.dev&apos;));
          } catch (e) {
            print(&apos;Error -&amp;gt; $e&apos;);
          }
        },
      )),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your app on the &lt;strong&gt;web&lt;/strong&gt; should look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_7_gr9gvvqov4.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;And when you tap the button..&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_8_kw9mhvznx0.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;And when you run it on &lt;strong&gt;iOS&lt;/strong&gt;/&lt;strong&gt;Android&lt;/strong&gt; it should look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_9_af3wexxmw2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;And when you tap the button..&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_10_c184w6lu7g.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Congratulations! You made it 🎉&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/xx_11_k1m9m1qs1w.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here is the final project located &lt;a href=&quot;https://github.com/rodydavis/flutter_x/tree/finish&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please reach out if you have any questions!&lt;/p&gt;
</content:encoded></item><item><title>Flutter + Fastlane (One Click Beta)</title><link>https://rodydavis.com/flutter/one-click-release/</link><guid isPermaLink="true">https://rodydavis.com/flutter/one-click-release/</guid><description>Automate the deployment of a Flutter application to both the iOS App Store and Google Play by following a step-by-step guide that integrates Fastlane.</description><pubDate>Mon, 20 Jan 2025 00:07:34 GMT</pubDate><content:encoded>&lt;h1&gt;Flutter + Fastlane (One Click Beta)&lt;/h1&gt;
&lt;h2&gt;1. Install Flutter&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://flutter.io/get-started/install/&quot;&gt;Download Flutter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/ff_1_u67ip3pbk5.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;2. Create new Flutter Project&lt;/h2&gt;
&lt;p&gt;If you are pretty new to Flutter you can check out &lt;a href=&quot;https://flutter.io/get-started/codelab/&quot;&gt;this useful guide&lt;/a&gt; on how to create a new project step by step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/ff_2_9dljl4hgqq.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;3. Create App in iTunes Connect&lt;/h2&gt;
&lt;p&gt;If you are not familiar with iTunes Connect, check out &lt;a href=&quot;https://clearbridgemobile.com/how-to-submit-an-app-to-the-app-store/&quot;&gt;this article&lt;/a&gt; for getting started and setting up your first app for the App Store.&lt;/p&gt;
&lt;h2&gt;4. Create App in Google Play&lt;/h2&gt;
&lt;p&gt;Setting up an app in the Google Play Console can be tricky, make sure to check out the &lt;a href=&quot;https://support.google.com/googleplay/android-developer/answer/113469?hl=en-GB&quot;&gt;official reference&lt;/a&gt; and &lt;a href=&quot;https://medium.com/mindorks/upload-your-first-android-app-on-play-store-step-by-step-ee0de9123ac0&quot;&gt;this guide&lt;/a&gt; if you are having trouble.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/ff_3_1z55io2svg.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;5. Navigate to Project &amp;gt; ios and Setup Fastlane&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.fastlane.tools/getting-started/ios/setup/&quot;&gt;Reference&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;6. Navigate to Project &amp;gt; android and Setup Fastlane&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.fastlane.tools/getting-started/android/setup/&quot;&gt;Reference&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;7. Update Fastlane Fastfiles for iOS and Android and Change accordingly for each platform&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Make sure to change &amp;quot;YOUR PROJECT PATH&amp;quot; to the path to your project in Finder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Only copy the correct platform code for each Fastfile. For example, &lt;code&gt;default_platform(:ios)&lt;/code&gt; for iOS and `default_platform(:android)1st for Android.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;update_fastlane

default_platform(:ios)

platform :ios do
  desc &amp;quot;Push a new beta build to TestFlight&amp;quot;
  lane :beta do
    increment_build_number(xcodeproj: &amp;quot;Runner.xcodeproj&amp;quot;)
    build_app(workspace: &amp;quot;Runner.xcworkspace&amp;quot;, scheme: &amp;quot;Runner&amp;quot;)
    upload_to_testflight(skip_waiting_for_build_processing: true)
  end
  desc &amp;quot;Push a new release build to the App Store&amp;quot;
  lane :release do  
    increment_build_number(xcodeproj: &amp;quot;Runner.xcodeproj&amp;quot;)
    build_app(workspace: &amp;quot;Runner.xcworkspace&amp;quot;, scheme: &amp;quot;Runner&amp;quot;)
    upload_to_app_store(submit_for_review: true,
                            automatic_release: true,
                            skip_screenshots: true,
                            force: true,
                            skip_waiting_for_build_processing: true)
  end
end


  
//YOUR PROJECT PATH &amp;gt; android &amp;gt; fastlane &amp;gt; Fastfile
default_platform(:android)

platform :android do
  desc &amp;quot;Runs all the tests&amp;quot;
  lane :test do
    gradle(task: &amp;quot;test&amp;quot;)
  end

  desc &amp;quot;Submit a new Build to Beta&amp;quot;
  lane :beta do
    gradle(task: &apos;clean&apos;)
    increment_version_code
    sh &amp;quot;cd YOUR PROJECT PATH &amp;amp;&amp;amp; flutter build apk&amp;quot;
    upload_to_play_store(
      track: &apos;beta&apos;,
      apk: &apos;../build/app/outputs/apk/release/app-release.apk&apos;,
      skip_upload_screenshots: true,
      skip_upload_images: true
    )
    # crashlytics
  end

  desc &amp;quot;Deploy a new version to the Google Play&amp;quot;
  lane :deploy do
    gradle(task: &apos;clean&apos;)
    increment_version_code
    sh &amp;quot;cd YOUR PROJECT PATH &amp;amp;&amp;amp; flutter build apk&amp;quot;
    upload_to_play_store(
      track: &apos;production&apos;,
      apk: &apos;../build/app/outputs/apk/release/app-release.apk&apos;,
      skip_upload_screenshots: true,
      skip_upload_images: true
    )
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;For Android &lt;code&gt;increment_version_code&lt;/code&gt; install here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes it will fail and you will need to run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bundle exec fastlane add_plugin increment_version_code&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For iOS &lt;code&gt;increment_build_number&lt;/code&gt; set up Generic Versioning by enabling the agvtool.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/ff_4_39myyg3bzm.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/xcblog/agvtool-automating-ios-build-and-version-numbers-454cab6f1bbe&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;8. Metadata (Optional)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;For iOS you can have Fastlane download all your apps existing metadata including screenshots from iTunes Connect. In terminal navigate to the project and run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;fastlane deliver download_metadata &amp;amp;&amp;amp; fastlane deliver download_screenshots&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For Android you can use &lt;a href=&quot;https://docs.fastlane.tools/actions/supply/&quot;&gt;Fastlane Supply&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;9. Open Automator&lt;/h2&gt;
&lt;p&gt;Right now everything is working just by the command line. If you navigate to your project in terminal by adding &amp;quot;cd &amp;quot; and dragging in the project folder and hitting Enter, you can type &amp;quot;cd ios &amp;amp;&amp;amp; fastlane beta&amp;quot; or &amp;quot;cd android &amp;amp;&amp;amp; fastlane beta&amp;quot; and both will run fastlane.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/ff_5_1esbyy658n.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you want to be able to submit your app to Google Play and the App Store with one click we will be using &lt;a href=&quot;http://www.applegazette.com/os-x/getting-started-automator-workflows-mac/&quot;&gt;Automator&lt;/a&gt;. Create a new Automator Application. And Search for &amp;quot;Ask for Confirmation&amp;quot; and &amp;quot;Run AppleScript&amp;quot; and drag in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/ff_6_krrntoi3wl.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here is the Script for beta and release. You will need to create a Automator Application for both Beta and Release for each app you want automated. Save it where ever you want and create an Alias to be but on the Desktop.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure to change &lt;strong&gt;YOUR PROJECT PATH&lt;/strong&gt; to the path to your project in Finder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hint: I have my automator application save in the Github Repo of my project for versioning and easy access for different projects.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;//Beta
on run {input, parameters}
	tell application &amp;quot;Terminal&amp;quot;
		activate
		do script &amp;quot;cd YOUR PROJECT PATH/android &amp;amp;&amp;amp; fastlane beta &amp;amp;&amp;amp; cd YOUR PROJECT PATH/ios &amp;amp;&amp;amp; fastlane beta&amp;quot;
	end tell
	tell application &amp;quot;System Events&amp;quot;
		try
			set visible of application process &amp;quot;Terminal&amp;quot; to false
		end try
	end tell
end run

//Release
on run {input, parameters}
	tell application &amp;quot;Terminal&amp;quot;
		activate
		do script &amp;quot;cd YOUR PROJECT PATH/android &amp;amp;&amp;amp; fastlane deploy &amp;amp;&amp;amp; cd YOUR PROJECT PATH/ios &amp;amp;&amp;amp; fastlane release&amp;quot;
	end tell
	tell application &amp;quot;System Events&amp;quot;
		try
			set visible of application process &amp;quot;Terminal&amp;quot; to false
		end try
	end tell
end run
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;10. Try It Out!&lt;/h2&gt;
&lt;p&gt;Everything should be working now. If you double click on the automator application you should get a confirmation pop up to release the app. The Script will run terminal in the background and you can stay focused on developing awesome flutter applications. If you want to see the progress on fastlane uploading your apps you can click on the terminal icon and the terminal window will reappear. Thanks for reading and please reach out for any questions you have!&lt;/p&gt;
</content:encoded></item><item><title>Signals and Flutter Hooks</title><link>https://rodydavis.com/flutter/signals-and-hooks/</link><guid isPermaLink="true">https://rodydavis.com/flutter/signals-and-hooks/</guid><description>The guide contrasts basic state management using `setState` with the more advanced, reactive approach of using `ValueNotifier` and `ValueListenableBuilder` to efficiently update specific parts of the Flutter widget tree.</description><pubDate>Fri, 31 Jan 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Signals and Flutter Hooks&lt;/h1&gt;
&lt;p&gt;When working with data in &lt;a href=&quot;https://flutter.dev&quot;&gt;Flutter&lt;/a&gt;, on of the first things you are exposed to is &lt;a href=&quot;https://api.flutter.dev/flutter/widgets/State/setState.html&quot;&gt;setState&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;setState&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State&amp;lt;Counter&amp;gt; createState() =&amp;gt; _CounterState();
}

class _CounterState extends State&amp;lt;Counter&amp;gt; {
  int count = 0;

  void increment() {
    if (mounted) {
      setState(() {
        count++;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text(&apos;Counter&apos;)),
      body: Center(child: Text(&apos;Count: $count&apos;)),
      floatingActionButton: FloatingActionButton(
        onPressed: increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simply marks the widget as dirty every time you call &lt;strong&gt;setState&lt;/strong&gt; but requires you (as the developer) to be mindful and explict about when those updates happen. If you forget to call &lt;strong&gt;setState&lt;/strong&gt; when mutating data the widget tree can become stale.&lt;/p&gt;
&lt;h2&gt;ValueNotifier&lt;/h2&gt;
&lt;p&gt;We can impove this by using &lt;a href=&quot;https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html&quot;&gt;ValueNotifier&lt;/a&gt; instead of storing the value directly. This gives us the ability to read and write a value in a container and use helper widgets like &lt;a href=&quot;https://api.flutter.dev/flutter/widgets/ValueListenableBuilder-class.html&quot;&gt;ValueListenableBuilder&lt;/a&gt; to update sub parts of the widget tree on value changes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State&amp;lt;Counter&amp;gt; createState() =&amp;gt; _CounterState();
}

class _CounterState extends State&amp;lt;Counter&amp;gt; {
  final count = ValueNotifier(0);

  void increment() {
    count.value++;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text(&apos;Counter&apos;)),
      body: Center(child: ValueListenableBuilder(
        valueListenable: count,
        builder: (context, value, child) {
          return Text(&apos;Count: $value&apos;);
        }
      )),
      floatingActionButton: FloatingActionButton(
        onPressed: increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FlutterSignal&lt;/h2&gt;
&lt;p&gt;Using the &lt;a href=&quot;https://pub.dev/packages/signals&quot;&gt;signals&lt;/a&gt; package we can upgrade ValueNotifier to a &lt;a href=&quot;https://preactjs.com/guide/v10/signals/&quot;&gt;signal backed implmentation&lt;/a&gt; which uses a reactive graph based on a push / pull architecture.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:signals/signals_flutter.dart&apos;;

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State&amp;lt;Counter&amp;gt; createState() =&amp;gt; _CounterState();
}

class _CounterState extends State&amp;lt;Counter&amp;gt; {
  final count = signal(0);

  void increment() {
    count.value++;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text(&apos;Counter&apos;)),
      body: Center(
        child: ValueListenableBuilder(
          valueListenable: count,
          builder: (context, value, child) {
            return Text(&apos;Count: $value&apos;);
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Signals created after &lt;strong&gt;6.0.0&lt;/strong&gt; also implement ValueNotifier so you can easily migrate them without changing any other code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead of ValueListenableBuilder we can use the Watch widget or .watch(context) extension.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:signals/signals_flutter.dart&apos;;

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State&amp;lt;Counter&amp;gt; createState() =&amp;gt; _CounterState();
}

class _CounterState extends State&amp;lt;Counter&amp;gt; {
  final count = signal(0);

  void increment() {
    count.value++;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text(&apos;Counter&apos;)),
      body: Center(
        child: Text(&apos;Count: ${count.watch(context)}&apos;),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;flutter_hooks&lt;/h2&gt;
&lt;p&gt;Using &lt;a href=&quot;https://pub.dev/packages/flutter_hooks&quot;&gt;Flutter Hooks&lt;/a&gt; we can reduce boilerplate of StatefulWidget by switching to a HookWidget. With &lt;strong&gt;useState&lt;/strong&gt; we can define the state directly in the build method and easily share them across widgets.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter_hooks/flutter_hooks.dart&apos;;

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

class Counter extends HookWidget {
  const Counter({super.key});

  @override
  Widget build(BuildContext context) {
    final count = useState(0);
    return Scaffold(
      appBar: AppBar(title: const Text(&apos;Counter&apos;)),
      body: Center(child: Text(&apos;Count: ${count.value}&apos;)),
      floatingActionButton: FloatingActionButton(
        onPressed: () =&amp;gt; count.value++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;useState&lt;/strong&gt; returns a ValueNotifier that automatically rebuilds the widget on changes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;signals_hooks&lt;/h2&gt;
&lt;p&gt;Using a new package &lt;a href=&quot;https://pub.dev/packages/signals_hooks&quot;&gt;signals_hooks&lt;/a&gt; we can now define signals in HookWidgets and have the benifits of a reactive graph with shareable lifecycles between widgets.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter_hooks/flutter_hooks.dart&apos;;
import &apos;package:signals_hooks/signals_hooks.dart&apos;;

void main() {
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}

class Counter extends HookWidget {
  const Counter({super.key});

  @override
  Widget build(BuildContext context) {
    final count = useSignal(0);
    final countStr = useComputed(() =&amp;gt; count.value.toString());
    useSignalEffect(() {
      print(&apos;count: $count&apos;);
    });
    return Scaffold(
      appBar: AppBar(title: const Text(&apos;Counter&apos;)),
      body: Center(child: Text(&apos;Count: $countStr&apos;)),
      floatingActionButton: FloatingActionButton(
        onPressed: () =&amp;gt; count.value++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Server Side Rendering Flutter Apps with RFW</title><link>https://rodydavis.com/flutter/ssr-rfw/</link><guid isPermaLink="true">https://rodydavis.com/flutter/ssr-rfw/</guid><description>A guide on implementing dynamic UI updates in Flutter applications using Server Side Rendering (SSR) and the rfw package for efficient binary data exchange.</description><pubDate>Mon, 20 Jan 2025 02:59:01 GMT</pubDate><content:encoded>&lt;h1&gt;Server Side Rendering Flutter Apps with RFW&lt;/h1&gt;
&lt;p&gt;This post will guide you how to build a Flutter app that takes advantage of Server Side Rendering (SSR) and being able to update UI dynamically.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are new to flutter you can follow this &lt;a href=&quot;https://rodydavis.com/posts/first-flutter-project&quot;&gt;post&lt;/a&gt; on getting started&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This technique will use the &lt;a href=&quot;https://pub.dev/packages/rfw&quot;&gt;rfw&lt;/a&gt; package on the server and client to send binary data via HTTP requests.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; You can find the final source &lt;a href=&quot;https://github.com/rodydavis/flutter_ssr&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Getting started&lt;/h2&gt;
&lt;p&gt;Create a new directory called &lt;code&gt;flutter_ssr&lt;/code&gt; and navigate to it in terminal or open it up in your favorite IDE.&lt;/p&gt;
&lt;h2&gt;Approaches to updates&lt;/h2&gt;
&lt;p&gt;If you are in the mobile world then you know how challenging it can be to get all your users on the latest version. Even just having an API or database schema can be very hard to update because of users on older versions (sometimes due to OS limitations).&lt;/p&gt;
&lt;h3&gt;Code Push&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://shorebird.dev/&quot;&gt;Shorebird&lt;/a&gt; takes an interesting approach to delivering updates to the users via Code Push and will update the apps live. This does have an advantage since it will update the UI and logic but what if the content update was only intended for a specific user or set of users?&lt;/p&gt;
&lt;h3&gt;Latest version supported&lt;/h3&gt;
&lt;p&gt;Another approach is to simply have an SLO/Policy that you only support X number of recent releases and that the app will not work on older versions.&lt;/p&gt;
&lt;p&gt;For something like this on mobile you would use &lt;a href=&quot;https://pub.dev/packages/upgrader&quot;&gt;upgrader&lt;/a&gt; via &lt;a href=&quot;https://sparkle-project.org/about/&quot;&gt;AppCast&lt;/a&gt; or &lt;a href=&quot;https://pub.dev/packages/in_app_update&quot;&gt;in_app_update&lt;/a&gt; to use Google Play APIs to update the app in the background or prevent using until updated.&lt;/p&gt;
&lt;p&gt;This has an advantage to know that users will be on the latest or no be supported and allow you to target newer APIs and roll updates easier. This does mean that users will be frustrated by updates more and that older devices may not be supported.&lt;/p&gt;
&lt;h3&gt;Server driven updates&lt;/h3&gt;
&lt;p&gt;Not all apps need to render data on the server and sometimes when building an offline first application you want to do everything local first, but this is for when you need to build a server first application. Here are some examples and use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bank accounts&lt;/li&gt;
&lt;li&gt;Airline / Hotel / Car booking&lt;/li&gt;
&lt;li&gt;Chat applications (Instant messaging)&lt;/li&gt;
&lt;li&gt;Marketing and AB testing&lt;/li&gt;
&lt;li&gt;Database first applications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these examples does not mean they are server only and in many cases you want to still cache the data locally to still offer a great offline experience.&lt;/p&gt;
&lt;p&gt;With Flutter you are building a runtime that you are shipping to the user as a Single Page Application (SPA) on the web and a mobile/desktop app on the stores. This means you need to ship all the logic and UI for every update.&lt;/p&gt;
&lt;p&gt;This has an advantage for doing more logic on the server and potentially really heavy requests are done server side and just the rendered UI is sent to the client. The client can still cache the response and allow for offline viewing too. These disadvantage here is that the client is expected to communicate with the server at some point and may not be suitable for offline only applications.&lt;/p&gt;
&lt;h2&gt;Remote Flutter Widgets (RFW)&lt;/h2&gt;
&lt;p&gt;The Flutter team has a package for creating widgets on the client and server and sending data necessary to connect them. This package is called &lt;a href=&quot;https://pub.dev/packages/rfw&quot;&gt;rfw&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While it is possible to ship logic in addition to UI as WASM that is out of scope for this post&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The rfw package uses a text format that can be compiled to binary and be used to represent state and dispatch events.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import core;
import material;

widget MaterialShop = Scaffold(
  appBar: AppBar(
    title: Text(text: [&apos;Products&apos;]),
  ),
  body: ListView(
    children: [
      ...for product in data.server.games:
        Product(product: product)
    ],
  ),
);

widget Product = ListTile(
  title: Text(text: args.product.name),
  onTap: event &apos;shop.productSelect&apos; { name: args.product.name, path: args.product.link },
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Multiple widgets can be defined and it may even look similar to the Dart API you are used to in Flutter but it is not quite the same.&lt;/p&gt;
&lt;p&gt;There are not logic branching blocks or conditional rendering but rather is a stateless format capable of updating every frame if needed.&lt;/p&gt;
&lt;p&gt;Take the following Flutter counter app that is generated when you create a new project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  @override
  State&amp;lt;MyHomePage&amp;gt; createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              &apos;You have pushed the button this many times:&apos;,
            ),
            Text(
              &apos;$_counter&apos;,
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: const Icon(Icons.add),
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could represent that in rfw like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import widgets;
import material;

widget root = Scaffold(
  appBar: AppBar(
    title: Text(text: [&apos;Counter Example&apos;]),
    centerTitle: true,
    backgroundColor: data.colorScheme.inversePrimary,
  ),
  body: Center(
    child: Column(
      mainAxisAlignment: &amp;quot;center&amp;quot;,
      children: [
        Text(text: [&amp;quot;You have pushed the button this many times:&amp;quot;]),
        Text(
          text: [data.counter.value],
          style: {
            fontSize: 20.0,
          },
        ),
      ],
    ),
  ),
  floatingActionButton: FloatingActionButton(
     onPressed: event &amp;quot;click&amp;quot; {},
     tooltip: [&amp;quot;Increment&amp;quot;],
     child: Icon(
        icon: 0xe047,
        fontFamily: &apos;MaterialIcons&apos;,
     ),
  ),
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may have noticed some arrays for strings and different ways of defining widgets. That is by design and the API will not be 100% with the Flutter SDK, but with the limitation comes different tradeoffs.&lt;/p&gt;
&lt;p&gt;You can define any custom widgets in your application and only uses the UI you have defined in your design system and the server will only be able to generate UI that you expect.&lt;/p&gt;
&lt;p&gt;It is also possible to create all the UI in the text format with rows, columns, containers and more.&lt;/p&gt;
&lt;h2&gt;Setting up the server&lt;/h2&gt;
&lt;p&gt;For this example we will be using &lt;a href=&quot;https://dartfrog.vgv.dev/docs/overview&quot;&gt;dart_frog&lt;/a&gt; to create the server application.&lt;/p&gt;
&lt;p&gt;In the directory that you created earlier run the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;dart_frog create server
flutter pub add rfw
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate the server boilerplate for us and add the correct dependencies.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Feel free to delete the test directory for now or update it later to check for the correct response.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Navigate to &lt;code&gt;/server/routes/index.dart&lt;/code&gt; and update the file with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:dart_frog/dart_frog.dart&apos;;
import &apos;package:rfw/formats.dart&apos;;

Response onRequest(RequestContext context) {
  var count = context.request.headers[&apos;COUNTER_VALUE&apos;] ?? &apos;0&apos;;

  if (context.request.method == HttpMethod.post) {
    count = (int.parse(count) + 1).toString();
  }

  return Response.bytes(
    body: encodeLibraryBlob(parseLibraryFile(template)),
    headers: {&apos;COUNTER_VALUE&apos;: count},
  );
}

const template = &apos;&apos;&apos;
import widgets;
import material;

widget root = Scaffold(
  appBar: AppBar(
    title: Text(text: [&apos;Counter Example&apos;]),
    centerTitle: true,
    backgroundColor: data.colorScheme.inversePrimary,
  ),
  body: Center(
    child: Column(
      mainAxisAlignment: &amp;quot;center&amp;quot;,
      children: [
        Text(text: [&amp;quot;You have pushed the button this many times:&amp;quot;]),
        Text(
          text: [data.counter.value],
          style: {
            fontSize: 20.0,
          },
        ),
      ],
    ),
  ),
  floatingActionButton: FloatingActionButton(
     onPressed: event &amp;quot;click&amp;quot; {},
     tooltip: [&amp;quot;Increment&amp;quot;],
     child: Icon(
        icon: 0xe047,
        fontFamily: &apos;MaterialIcons&apos;,
     ),
  ),
);
&apos;&apos;&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This takes the rfw text format we defined earlier and adds it to a string template.&lt;/p&gt;
&lt;p&gt;Using that template we can call &lt;code&gt;encodeLibraryBlob(parseLibraryFile(template))&lt;/code&gt; to create a binary representation of the text format.&lt;/p&gt;
&lt;p&gt;We are also checking for the &lt;code&gt;COUNTER_VALUE&lt;/code&gt; from the header to send state to/from the client. Since servers are stateless or easier to scale when they are this will help with not needing a type of session storage or context.&lt;/p&gt;
&lt;p&gt;If you navigate inside the &lt;code&gt;server&lt;/code&gt; directly you can start the dev server that will be needed for the next step:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;dart_frog dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;✓ Running on http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Setting up the client&lt;/h2&gt;
&lt;p&gt;In a new terminal tab you can navigate to the root of the directory and run the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter create app
flutter pub add rfw http
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate the counter app boilerplate and add the correct dependencies for us.&lt;/p&gt;
&lt;p&gt;The rfw package comes with &lt;strong&gt;core&lt;/strong&gt; and &lt;strong&gt;material&lt;/strong&gt; widgets but for this example we will be adding them manually to show how they are being called and created.&lt;/p&gt;
&lt;p&gt;Create and update the following file located att &lt;code&gt;app/lib/rfw/decoders.dart&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:rfw/rfw.dart&apos;;

class CustomArgumentDecoders {
  static ButtonStyle? outlinedButtonStyle(
    DataSource source,
    List&amp;lt;Object&amp;gt; key,
    BuildContext context,
  ) {
    if (!source.isMap(key)) {
      return null;
    }
    return OutlinedButton.styleFrom(
      foregroundColor: ArgumentDecoders.color(source, [&apos;foregroundColor&apos;]),
      backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
      disabledForegroundColor:
          ArgumentDecoders.color(source, [...key, &apos;disabledForegroundColor&apos;]),
      disabledBackgroundColor:
          ArgumentDecoders.color(source, [...key, &apos;disabledBackgroundColor&apos;]),
      shadowColor: ArgumentDecoders.color(source, [...key, &apos;shadowColor&apos;]),
      surfaceTintColor:
          ArgumentDecoders.color(source, [...key, &apos;surfaceTintColor&apos;]),
      elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
      textStyle: ArgumentDecoders.textStyle(source, [...key, &apos;textStyle&apos;]),
      padding: ArgumentDecoders.edgeInsets(source, [...key, &apos;padding&apos;]),
      minimumSize: CustomArgumentDecoders.size(source, [...key, &apos;minimumSize&apos;]),
      fixedSize: CustomArgumentDecoders.size(source, [...key, &apos;fixedSize&apos;]),
      maximumSize: CustomArgumentDecoders.size(source, [...key, &apos;maximumSize&apos;]),
      side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]),
      shape: CustomArgumentDecoders.outlinedBorder(source, [...key, &apos;shape&apos;]),
      enabledMouseCursor: CustomArgumentDecoders.mouseCursor(
          source, [...key, &apos;enabledMouseCursor&apos;]),
      disabledMouseCursor: CustomArgumentDecoders.mouseCursor(
          source, [...key, &apos;disabledMouseCursor&apos;]),
      visualDensity:
          ArgumentDecoders.visualDensity(source, [...key, &apos;visualDensity&apos;]),
      tapTargetSize: ArgumentDecoders.enumValue&amp;lt;MaterialTapTargetSize&amp;gt;(
              MaterialTapTargetSize.values,
              source,
              [...key, &apos;tapTargetSize&apos;]) ??
          MaterialTapTargetSize.shrinkWrap,
      animationDuration: ArgumentDecoders.duration(
          source, [...key, &apos;animationDuration&apos;], context),
      enableFeedback: source.v&amp;lt;bool&amp;gt;([...key, &apos;enableFeedback&apos;]),
      alignment: ArgumentDecoders.alignment(source, [...key, &apos;alignment&apos;]),
    );
  }

  static ButtonStyle? filledButtonStyle(
    DataSource source,
    List&amp;lt;Object&amp;gt; key,
    BuildContext context,
  ) {
    if (!source.isMap(key)) {
      return null;
    }
    return FilledButton.styleFrom(
      foregroundColor: ArgumentDecoders.color(source, [&apos;foregroundColor&apos;]),
      backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
      disabledForegroundColor:
          ArgumentDecoders.color(source, [...key, &apos;disabledForegroundColor&apos;]),
      disabledBackgroundColor:
          ArgumentDecoders.color(source, [...key, &apos;disabledBackgroundColor&apos;]),
      shadowColor: ArgumentDecoders.color(source, [...key, &apos;shadowColor&apos;]),
      surfaceTintColor:
          ArgumentDecoders.color(source, [...key, &apos;surfaceTintColor&apos;]),
      elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
      textStyle: ArgumentDecoders.textStyle(source, [...key, &apos;textStyle&apos;]),
      padding: ArgumentDecoders.edgeInsets(source, [...key, &apos;padding&apos;]),
      minimumSize: CustomArgumentDecoders.size(source, [...key, &apos;minimumSize&apos;]),
      fixedSize: CustomArgumentDecoders.size(source, [...key, &apos;fixedSize&apos;]),
      maximumSize: CustomArgumentDecoders.size(source, [...key, &apos;maximumSize&apos;]),
      side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]),
      shape: CustomArgumentDecoders.outlinedBorder(source, [...key, &apos;shape&apos;]),
      enabledMouseCursor: CustomArgumentDecoders.mouseCursor(
          source, [...key, &apos;enabledMouseCursor&apos;]),
      disabledMouseCursor: CustomArgumentDecoders.mouseCursor(
          source, [...key, &apos;disabledMouseCursor&apos;]),
      visualDensity:
          ArgumentDecoders.visualDensity(source, [...key, &apos;visualDensity&apos;]),
      tapTargetSize: ArgumentDecoders.enumValue&amp;lt;MaterialTapTargetSize&amp;gt;(
              MaterialTapTargetSize.values,
              source,
              [...key, &apos;tapTargetSize&apos;]) ??
          MaterialTapTargetSize.shrinkWrap,
      animationDuration: ArgumentDecoders.duration(
          source, [...key, &apos;animationDuration&apos;], context),
      enableFeedback: source.v&amp;lt;bool&amp;gt;([...key, &apos;enableFeedback&apos;]),
      alignment: ArgumentDecoders.alignment(source, [...key, &apos;alignment&apos;]),
    );
  }

  static ButtonStyle? textButtonStyle(
    DataSource source,
    List&amp;lt;Object&amp;gt; key,
    BuildContext context,
  ) {
    if (!source.isMap(key)) {
      return null;
    }
    return TextButton.styleFrom(
      foregroundColor: ArgumentDecoders.color(source, [&apos;foregroundColor&apos;]),
      backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
      disabledForegroundColor:
          ArgumentDecoders.color(source, [...key, &apos;disabledForegroundColor&apos;]),
      disabledBackgroundColor:
          ArgumentDecoders.color(source, [...key, &apos;disabledBackgroundColor&apos;]),
      shadowColor: ArgumentDecoders.color(source, [...key, &apos;shadowColor&apos;]),
      surfaceTintColor:
          ArgumentDecoders.color(source, [...key, &apos;surfaceTintColor&apos;]),
      elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
      textStyle: ArgumentDecoders.textStyle(source, [...key, &apos;textStyle&apos;]),
      padding: ArgumentDecoders.edgeInsets(source, [...key, &apos;padding&apos;]),
      minimumSize: CustomArgumentDecoders.size(source, [...key, &apos;minimumSize&apos;]),
      fixedSize: CustomArgumentDecoders.size(source, [...key, &apos;fixedSize&apos;]),
      maximumSize: CustomArgumentDecoders.size(source, [...key, &apos;maximumSize&apos;]),
      side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]),
      shape: CustomArgumentDecoders.outlinedBorder(source, [...key, &apos;shape&apos;]),
      enabledMouseCursor: CustomArgumentDecoders.mouseCursor(
          source, [...key, &apos;enabledMouseCursor&apos;]),
      disabledMouseCursor: CustomArgumentDecoders.mouseCursor(
          source, [...key, &apos;disabledMouseCursor&apos;]),
      visualDensity:
          ArgumentDecoders.visualDensity(source, [...key, &apos;visualDensity&apos;]),
      tapTargetSize: ArgumentDecoders.enumValue&amp;lt;MaterialTapTargetSize&amp;gt;(
              MaterialTapTargetSize.values,
              source,
              [...key, &apos;tapTargetSize&apos;]) ??
          MaterialTapTargetSize.shrinkWrap,
      animationDuration: ArgumentDecoders.duration(
          source, [...key, &apos;animationDuration&apos;], context),
      enableFeedback: source.v&amp;lt;bool&amp;gt;([...key, &apos;enableFeedback&apos;]),
      alignment: ArgumentDecoders.alignment(source, [...key, &apos;alignment&apos;]),
    );
  }

  static EdgeInsets? edgeInsets(DataSource source, List&amp;lt;Object&amp;gt; key) {
    if (!source.isMap(key)) {
      return null;
    }
    final all = source.v&amp;lt;double&amp;gt;([...key, &apos;all&apos;]);
    if (all != null) return EdgeInsets.all(all);
    final vertical = source.v&amp;lt;double&amp;gt;([...key, &apos;vertical&apos;]);
    final horizontal = source.v&amp;lt;double&amp;gt;([...key, &apos;horizontal&apos;]);
    if (vertical != null || horizontal != null) {
      return EdgeInsets.symmetric(
        vertical: vertical ?? 0,
        horizontal: horizontal ?? 0,
      );
    }
    final top = source.v&amp;lt;double&amp;gt;([...key, &apos;top&apos;]);
    final bottom = source.v&amp;lt;double&amp;gt;([...key, &apos;bottom&apos;]);
    final left = source.v&amp;lt;double&amp;gt;([...key, &apos;left&apos;]);
    final right = source.v&amp;lt;double&amp;gt;([...key, &apos;right&apos;]);
    return EdgeInsets.only(
      top: top ?? 0,
      bottom: bottom ?? 0,
      left: left ?? 0,
      right: right ?? 0,
    );
  }

  static Size? size(DataSource source, List&amp;lt;Object&amp;gt; key) {
    if (!source.isMap(key)) {
      return null;
    }
    return Size(
      source.v&amp;lt;double&amp;gt;([...key, &apos;width&apos;]) ?? 0.0,
      source.v&amp;lt;double&amp;gt;([...key, &apos;height&apos;]) ?? 0.0,
    );
  }

  static MouseCursor? mouseCursor(DataSource source, List&amp;lt;Object&amp;gt; key) {
    if (!source.isMap(key)) {
      return null;
    }
    final type = source.v&amp;lt;String&amp;gt;([...key, &apos;type&apos;]);
    final value = source.v&amp;lt;String&amp;gt;([...key, &apos;value&apos;]);
    if (type == &apos;system&apos;) {
      switch (value) {
        case &apos;alias&apos;:
          return SystemMouseCursors.alias;
        case &apos;none&apos;:
          return SystemMouseCursors.none;
        case &apos;basic&apos;:
          return SystemMouseCursors.basic;
        case &apos;click&apos;:
          return SystemMouseCursors.click;
        case &apos;forbidden&apos;:
          return SystemMouseCursors.forbidden;
        case &apos;wait&apos;:
          return SystemMouseCursors.wait;
        case &apos;progress&apos;:
          return SystemMouseCursors.progress;
        case &apos;contextMenu&apos;:
          return SystemMouseCursors.contextMenu;
        case &apos;help&apos;:
          return SystemMouseCursors.help;
        case &apos;text&apos;:
          return SystemMouseCursors.text;
        case &apos;verticalText&apos;:
          return SystemMouseCursors.verticalText;
        case &apos;cell&apos;:
          return SystemMouseCursors.cell;
        case &apos;precise&apos;:
          return SystemMouseCursors.precise;
        case &apos;move&apos;:
          return SystemMouseCursors.move;
        case &apos;grab&apos;:
          return SystemMouseCursors.grab;
        case &apos;grabbing&apos;:
          return SystemMouseCursors.grabbing;
        case &apos;noDrop&apos;:
          return SystemMouseCursors.noDrop;
        case &apos;alias&apos;:
          return SystemMouseCursors.alias;
        case &apos;copy&apos;:
          return SystemMouseCursors.copy;
        case &apos;disappearing&apos;:
          return SystemMouseCursors.disappearing;
        case &apos;allScroll&apos;:
          return SystemMouseCursors.allScroll;
        case &apos;resizeLeftRight&apos;:
          return SystemMouseCursors.resizeLeftRight;
        case &apos;resizeUpDown&apos;:
          return SystemMouseCursors.resizeUpDown;
        case &apos;resizeUpLeftDownRight&apos;:
          return SystemMouseCursors.resizeUpLeftDownRight;
        case &apos;resizeUpRightDownLeft&apos;:
          return SystemMouseCursors.resizeUpRightDownLeft;
        case &apos;resizeUp&apos;:
          return SystemMouseCursors.resizeUp;
        case &apos;resizeDown&apos;:
          return SystemMouseCursors.resizeDown;
        case &apos;resizeLeft&apos;:
          return SystemMouseCursors.resizeLeft;
        case &apos;resizeRight&apos;:
          return SystemMouseCursors.resizeRight;
        case &apos;resizeUpLeft&apos;:
          return SystemMouseCursors.resizeUpLeft;
        case &apos;resizeUpRight&apos;:
          return SystemMouseCursors.resizeUpRight;
        case &apos;resizeDownLeft&apos;:
          return SystemMouseCursors.resizeDownLeft;
        case &apos;resizeDownRight&apos;:
          return SystemMouseCursors.resizeDownRight;
        case &apos;resizeColumn&apos;:
          return SystemMouseCursors.resizeColumn;
        case &apos;resizeRow&apos;:
          return SystemMouseCursors.resizeRow;
        case &apos;zoomIn&apos;:
          return SystemMouseCursors.zoomIn;
        case &apos;zoomOut&apos;:
          return SystemMouseCursors.zoomOut;
        default:
      }
    }
    return null;
  }

  static LinearBorderEdge? linearBorderEdge(
      DataSource source, List&amp;lt;Object&amp;gt; key) {
    if (!source.isMap(key)) {
      return null;
    }
    return LinearBorderEdge(
      size: source.v&amp;lt;double&amp;gt;([...key, &apos;size&apos;]) ?? 1.0,
      alignment: source.v&amp;lt;double&amp;gt;([...key, &apos;alignment&apos;]) ?? 0.0,
    );
  }

  static OutlinedBorder? outlinedBorder(DataSource source, List&amp;lt;Object&amp;gt; key) {
    if (!source.isMap(key)) {
      return null;
    }
    final type = source.v&amp;lt;String&amp;gt;([...key, &apos;type&apos;]);
    switch (type) {
      case &amp;quot;RoundedRectangleBorder&amp;quot;:
        return RoundedRectangleBorder(
          side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]) ??
              BorderSide.none,
          borderRadius:
              ArgumentDecoders.borderRadius(source, [...key, &apos;borderRadius&apos;]) ??
                  BorderRadius.zero,
        );
      case &amp;quot;BeveledRectangleBorder&amp;quot;:
        return BeveledRectangleBorder(
          side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]) ??
              BorderSide.none,
          borderRadius:
              ArgumentDecoders.borderRadius(source, [...key, &apos;borderRadius&apos;]) ??
                  BorderRadius.zero,
        );
      case &amp;quot;ContinuousRectangleBorder&amp;quot;:
        return ContinuousRectangleBorder(
          side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]) ??
              BorderSide.none,
          borderRadius:
              ArgumentDecoders.borderRadius(source, [...key, &apos;borderRadius&apos;]) ??
                  BorderRadius.zero,
        );
      case &amp;quot;CircleBorder&amp;quot;:
        return CircleBorder(
          side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]) ??
              BorderSide.none,
          eccentricity: source.v&amp;lt;double&amp;gt;([...key, &apos;eccentricity&apos;]) ?? 0.0,
        );
      case &amp;quot;LinearBorder&amp;quot;:
        return LinearBorder(
          side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]) ??
              BorderSide.none,
          start: CustomArgumentDecoders.linearBorderEdge(
              source, [...key, &apos;start&apos;]),
          end: CustomArgumentDecoders.linearBorderEdge(source, [...key, &apos;end&apos;]),
          top: CustomArgumentDecoders.linearBorderEdge(source, [...key, &apos;top&apos;]),
          bottom: CustomArgumentDecoders.linearBorderEdge(
              source, [...key, &apos;bottom&apos;]),
        );
      case &amp;quot;OvalBorder&amp;quot;:
        return OvalBorder(
          side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]) ??
              BorderSide.none,
          eccentricity: source.v&amp;lt;double&amp;gt;([...key, &apos;eccentricity&apos;]) ?? 0.0,
        );
      case &amp;quot;StadiumBorder&amp;quot;:
        return StadiumBorder(
          side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]) ??
              BorderSide.none,
        );
      case &amp;quot;StarBorder&amp;quot;:
        return StarBorder(
          side: ArgumentDecoders.borderSide(source, [...key, &apos;side&apos;]) ??
              BorderSide.none,
          points: source.v&amp;lt;double&amp;gt;([...key, &apos;points&apos;]) ?? 5.0,
          innerRadiusRatio:
              source.v&amp;lt;double&amp;gt;([...key, &apos;innerRadiusRatio&apos;]) ?? 0.4,
          pointRounding: source.v&amp;lt;double&amp;gt;([...key, &apos;pointRounding&apos;]) ?? 0.0,
          valleyRounding: source.v&amp;lt;double&amp;gt;([...key, &apos;valleyRounding&apos;]) ?? 0.0,
          rotation: source.v&amp;lt;double&amp;gt;([...key, &apos;rotation&apos;]) ?? 0.0,
          squash: source.v&amp;lt;double&amp;gt;([...key, &apos;squash&apos;]) ?? 0.0,
        );
      default:
        break;
    }
    return null;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates the custom decoders needed for the widgets we are about to define.&lt;/p&gt;
&lt;h3&gt;Defining the core library&lt;/h3&gt;
&lt;p&gt;Create and update the following file located at &lt;code&gt;app/lib/rfw/core.dart&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/gestures.dart&apos;;
import &apos;package:flutter/material.dart&apos;;
import &apos;package:rfw/rfw.dart&apos;;

LocalWidgetLibrary createCoreWidgets() =&amp;gt;
    LocalWidgetLibrary(_coreWidgetsDefinitions);

Map&amp;lt;String, LocalWidgetBuilder&amp;gt; get _coreWidgetsDefinitions =&amp;gt;
    &amp;lt;String, LocalWidgetBuilder&amp;gt;{
      &apos;AnimationDefaults&apos;: (BuildContext context, DataSource source) {
        return AnimationDefaults(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;Align&apos;: (BuildContext context, DataSource source) {
        return AnimatedAlign(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          alignment: ArgumentDecoders.alignment(source, [&apos;alignment&apos;]) ??
              Alignment.center,
          widthFactor: source.v&amp;lt;double&amp;gt;([&apos;widthFactor&apos;]),
          heightFactor: source.v&amp;lt;double&amp;gt;([&apos;heightFactor&apos;]),
          onEnd: source.voidHandler([&apos;onEnd&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;AspectRatio&apos;: (BuildContext context, DataSource source) {
        return AspectRatio(
          aspectRatio: source.v&amp;lt;double&amp;gt;([&apos;aspectRatio&apos;]) ?? 1.0,
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;Center&apos;: (BuildContext context, DataSource source) {
        return Center(
          widthFactor: source.v&amp;lt;double&amp;gt;([&apos;widthFactor&apos;]),
          heightFactor: source.v&amp;lt;double&amp;gt;([&apos;heightFactor&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;ColoredBox&apos;: (BuildContext context, DataSource source) {
        return ColoredBox(
          color: ArgumentDecoders.color(source, [&apos;color&apos;]) ??
              const Color(0xFF000000),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;Column&apos;: (BuildContext context, DataSource source) {
        return Column(
          mainAxisAlignment: ArgumentDecoders.enumValue&amp;lt;MainAxisAlignment&amp;gt;(
                  MainAxisAlignment.values, source, [&apos;mainAxisAlignment&apos;]) ??
              MainAxisAlignment.start,
          mainAxisSize: ArgumentDecoders.enumValue&amp;lt;MainAxisSize&amp;gt;(
                  MainAxisSize.values, source, [&apos;mainAxisSize&apos;]) ??
              MainAxisSize.max,
          crossAxisAlignment: ArgumentDecoders.enumValue&amp;lt;CrossAxisAlignment&amp;gt;(
                  CrossAxisAlignment.values, source, [&apos;crossAxisAlignment&apos;]) ??
              CrossAxisAlignment.center,
          textDirection: ArgumentDecoders.enumValue&amp;lt;TextDirection&amp;gt;(
              TextDirection.values, source, [&apos;textDirection&apos;]),
          verticalDirection: ArgumentDecoders.enumValue&amp;lt;VerticalDirection&amp;gt;(
                  VerticalDirection.values, source, [&apos;verticalDirection&apos;]) ??
              VerticalDirection.down,
          textBaseline: ArgumentDecoders.enumValue&amp;lt;TextBaseline&amp;gt;(
              TextBaseline.values, source, [&apos;textBaseline&apos;]),
          children: source.childList([&apos;children&apos;]),
        );
      },
      &apos;Container&apos;: (BuildContext context, DataSource source) {
        return AnimatedContainer(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          alignment: ArgumentDecoders.alignment(source, [&apos;alignment&apos;]),
          padding: ArgumentDecoders.edgeInsets(source, [&apos;padding&apos;]),
          color: ArgumentDecoders.color(source, [&apos;color&apos;]),
          decoration: ArgumentDecoders.decoration(source, [&apos;decoration&apos;]),
          foregroundDecoration:
              ArgumentDecoders.decoration(source, [&apos;foregroundDecoration&apos;]),
          width: source.v&amp;lt;double&amp;gt;([&apos;width&apos;]),
          height: source.v&amp;lt;double&amp;gt;([&apos;height&apos;]),
          constraints: ArgumentDecoders.boxConstraints(source, [&apos;constraints&apos;]),
          margin: ArgumentDecoders.edgeInsets(source, [&apos;margin&apos;]),
          transform: ArgumentDecoders.matrix(source, [&apos;transform&apos;]),
          transformAlignment:
              ArgumentDecoders.alignment(source, [&apos;transformAlignment&apos;]),
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          onEnd: source.voidHandler([&apos;onEnd&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;DefaultTextStyle&apos;: (BuildContext context, DataSource source) {
        return AnimatedDefaultTextStyle(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          style: ArgumentDecoders.textStyle(source, [&apos;style&apos;]) ??
              const TextStyle(),
          textAlign: ArgumentDecoders.enumValue&amp;lt;TextAlign&amp;gt;(
              TextAlign.values, source, [&apos;textAlign&apos;]),
          softWrap: source.v&amp;lt;bool&amp;gt;([&apos;softWrap&apos;]) ?? true,
          overflow: ArgumentDecoders.enumValue&amp;lt;TextOverflow&amp;gt;(
                  TextOverflow.values, source, [&apos;overflow&apos;]) ??
              TextOverflow.clip,
          maxLines: source.v&amp;lt;int&amp;gt;([&apos;maxLines&apos;]),
          textWidthBasis: ArgumentDecoders.enumValue&amp;lt;TextWidthBasis&amp;gt;(
                  TextWidthBasis.values, source, [&apos;textWidthBasis&apos;]) ??
              TextWidthBasis.parent,
          textHeightBehavior: ArgumentDecoders.textHeightBehavior(
              source, [&apos;textHeightBehavior&apos;]),
          onEnd: source.voidHandler([&apos;onEnd&apos;]),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;Directionality&apos;: (BuildContext context, DataSource source) {
        return Directionality(
          textDirection: ArgumentDecoders.enumValue&amp;lt;TextDirection&amp;gt;(
                  TextDirection.values, source, [&apos;textDirection&apos;]) ??
              TextDirection.ltr,
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;Expanded&apos;: (BuildContext context, DataSource source) {
        return Expanded(
          flex: source.v&amp;lt;int&amp;gt;([&apos;flex&apos;]) ?? 1,
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;FittedBox&apos;: (BuildContext context, DataSource source) {
        return FittedBox(
          fit: ArgumentDecoders.enumValue&amp;lt;BoxFit&amp;gt;(
                  BoxFit.values, source, [&apos;fit&apos;]) ??
              BoxFit.contain,
          alignment: ArgumentDecoders.alignment(source, [&apos;alignment&apos;]) ??
              Alignment.center,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;FractionallySizedBox&apos;: (BuildContext context, DataSource source) {
        return FractionallySizedBox(
          alignment: ArgumentDecoders.alignment(source, [&apos;alignment&apos;]) ??
              Alignment.center,
          widthFactor: source.v&amp;lt;double&amp;gt;([&apos;widthFactor&apos;]),
          heightFactor: source.v&amp;lt;double&amp;gt;([&apos;heightFactor&apos;]),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;GestureDetector&apos;: (BuildContext context, DataSource source) {
        return GestureDetector(
          onTap: source.voidHandler([&apos;onTap&apos;]),
          onTapDown: source.handler([&apos;onTapDown&apos;],
              (VoidCallback trigger) =&amp;gt; (TapDownDetails details) =&amp;gt; trigger()),
          onTapUp: source.handler([&apos;onTapUp&apos;],
              (VoidCallback trigger) =&amp;gt; (TapUpDetails details) =&amp;gt; trigger()),
          onTapCancel: source.voidHandler([&apos;onTapCancel&apos;]),
          onDoubleTap: source.voidHandler([&apos;onDoubleTap&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          behavior: ArgumentDecoders.enumValue&amp;lt;HitTestBehavior&amp;gt;(
              HitTestBehavior.values, source, [&apos;behavior&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;GridView&apos;: (BuildContext context, DataSource source) {
        return GridView.builder(
          scrollDirection: ArgumentDecoders.enumValue&amp;lt;Axis&amp;gt;(
                  Axis.values, source, [&apos;scrollDirection&apos;]) ??
              Axis.vertical,
          reverse: source.v&amp;lt;bool&amp;gt;([&apos;reverse&apos;]) ?? false,
          primary: source.v&amp;lt;bool&amp;gt;([&apos;primary&apos;]),
          shrinkWrap: source.v&amp;lt;bool&amp;gt;([&apos;shrinkWrap&apos;]) ?? false,
          padding: ArgumentDecoders.edgeInsets(source, [&apos;padding&apos;]),
          gridDelegate:
              ArgumentDecoders.gridDelegate(source, [&apos;gridDelegate&apos;]) ??
                  const SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 2),
          itemBuilder: (BuildContext context, int index) =&amp;gt;
              source.child([&apos;children&apos;, index]),
          itemCount: source.length([&apos;children&apos;]),
          addAutomaticKeepAlives:
              source.v&amp;lt;bool&amp;gt;([&apos;addAutomaticKeepAlives&apos;]) ?? true,
          addRepaintBoundaries:
              source.v&amp;lt;bool&amp;gt;([&apos;addRepaintBoundaries&apos;]) ?? true,
          addSemanticIndexes: source.v&amp;lt;bool&amp;gt;([&apos;addSemanticIndexes&apos;]) ?? true,
          cacheExtent: source.v&amp;lt;double&amp;gt;([&apos;cacheExtent&apos;]),
          semanticChildCount: source.v&amp;lt;int&amp;gt;([&apos;semanticChildCount&apos;]),
          dragStartBehavior: ArgumentDecoders.enumValue&amp;lt;DragStartBehavior&amp;gt;(
                  DragStartBehavior.values, source, [&apos;dragStartBehavior&apos;]) ??
              DragStartBehavior.start,
          keyboardDismissBehavior:
              ArgumentDecoders.enumValue&amp;lt;ScrollViewKeyboardDismissBehavior&amp;gt;(
                      ScrollViewKeyboardDismissBehavior.values,
                      source,
                      [&apos;keyboardDismissBehavior&apos;]) ??
                  ScrollViewKeyboardDismissBehavior.manual,
          restorationId: source.v&amp;lt;String&amp;gt;([&apos;restorationId&apos;]),
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.hardEdge,
        );
      },
      &apos;Icon&apos;: (BuildContext context, DataSource source) {
        return Icon(
          ArgumentDecoders.iconData(source, []) ?? Icons.flutter_dash,
          size: source.v&amp;lt;double&amp;gt;([&apos;size&apos;]),
          color: ArgumentDecoders.color(source, [&apos;color&apos;]),
          semanticLabel: source.v&amp;lt;String&amp;gt;([&apos;semanticLabel&apos;]),
          textDirection: ArgumentDecoders.enumValue&amp;lt;TextDirection&amp;gt;(
              TextDirection.values, source, [&apos;textDirection&apos;]),
        );
      },
      &apos;IconTheme&apos;: (BuildContext context, DataSource source) {
        return IconTheme(
          data: ArgumentDecoders.iconThemeData(source, []) ??
              const IconThemeData(),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;IntrinsicHeight&apos;: (BuildContext context, DataSource source) {
        return IntrinsicHeight(
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;IntrinsicWidth&apos;: (BuildContext context, DataSource source) {
        return IntrinsicWidth(
          stepWidth: source.v&amp;lt;double&amp;gt;([&apos;width&apos;]),
          stepHeight: source.v&amp;lt;double&amp;gt;([&apos;height&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;Image&apos;: (BuildContext context, DataSource source) {
        return Image(
          image: ArgumentDecoders.imageProvider(source, []) ??
              const AssetImage(&apos;error.png&apos;),
          semanticLabel: source.v&amp;lt;String&amp;gt;([&apos;semanticLabel&apos;]),
          excludeFromSemantics:
              source.v&amp;lt;bool&amp;gt;([&apos;excludeFromSemantics&apos;]) ?? false,
          width: source.v&amp;lt;double&amp;gt;([&apos;width&apos;]),
          height: source.v&amp;lt;double&amp;gt;([&apos;height&apos;]),
          color: ArgumentDecoders.color(source, [&apos;color&apos;]),
          colorBlendMode: ArgumentDecoders.enumValue&amp;lt;BlendMode&amp;gt;(
              BlendMode.values, source, [&apos;blendMode&apos;]),
          fit: ArgumentDecoders.enumValue&amp;lt;BoxFit&amp;gt;(
              BoxFit.values, source, [&apos;fit&apos;]),
          alignment: ArgumentDecoders.alignment(source, [&apos;alignment&apos;]) ??
              Alignment.center,
          repeat: ArgumentDecoders.enumValue&amp;lt;ImageRepeat&amp;gt;(
                  ImageRepeat.values, source, [&apos;repeat&apos;]) ??
              ImageRepeat.noRepeat,
          centerSlice: ArgumentDecoders.rect(source, [&apos;centerSlice&apos;]),
          matchTextDirection: source.v&amp;lt;bool&amp;gt;([&apos;matchTextDirection&apos;]) ?? false,
          gaplessPlayback: source.v&amp;lt;bool&amp;gt;([&apos;gaplessPlayback&apos;]) ?? false,
          isAntiAlias: source.v&amp;lt;bool&amp;gt;([&apos;isAntiAlias&apos;]) ?? false,
          filterQuality: ArgumentDecoders.enumValue&amp;lt;FilterQuality&amp;gt;(
                  FilterQuality.values, source, [&apos;filterQuality&apos;]) ??
              FilterQuality.low,
        );
      },
      &apos;ListBody&apos;: (BuildContext context, DataSource source) {
        return ListBody(
          mainAxis: ArgumentDecoders.enumValue&amp;lt;Axis&amp;gt;(
                  Axis.values, source, [&apos;mainAxis&apos;]) ??
              Axis.vertical,
          reverse: source.v&amp;lt;bool&amp;gt;([&apos;reverse&apos;]) ?? false,
          children: source.childList([&apos;children&apos;]),
        );
      },
      &apos;ListView&apos;: (BuildContext context, DataSource source) {
        return ListView.builder(
          scrollDirection: ArgumentDecoders.enumValue&amp;lt;Axis&amp;gt;(
                  Axis.values, source, [&apos;scrollDirection&apos;]) ??
              Axis.vertical,
          reverse: source.v&amp;lt;bool&amp;gt;([&apos;reverse&apos;]) ?? false,
          primary: source.v&amp;lt;bool&amp;gt;([&apos;primary&apos;]),
          shrinkWrap: source.v&amp;lt;bool&amp;gt;([&apos;shrinkWrap&apos;]) ?? false,
          padding: ArgumentDecoders.edgeInsets(source, [&apos;padding&apos;]),
          itemExtent: source.v&amp;lt;double&amp;gt;([&apos;itemExtent&apos;]),
          prototypeItem: source.optionalChild([&apos;prototypeItem&apos;]),
          itemCount: source.length([&apos;children&apos;]),
          itemBuilder: (BuildContext context, int index) =&amp;gt;
              source.child([&apos;children&apos;, index]),
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.hardEdge,
          addAutomaticKeepAlives:
              source.v&amp;lt;bool&amp;gt;([&apos;addAutomaticKeepAlives&apos;]) ?? true,
          addRepaintBoundaries:
              source.v&amp;lt;bool&amp;gt;([&apos;addRepaintBoundaries&apos;]) ?? true,
          addSemanticIndexes: source.v&amp;lt;bool&amp;gt;([&apos;addSemanticIndexes&apos;]) ?? true,
          cacheExtent: source.v&amp;lt;double&amp;gt;([&apos;cacheExtent&apos;]),
          semanticChildCount: source.v&amp;lt;int&amp;gt;([&apos;semanticChildCount&apos;]),
          dragStartBehavior: ArgumentDecoders.enumValue&amp;lt;DragStartBehavior&amp;gt;(
                  DragStartBehavior.values, source, [&apos;dragStartBehavior&apos;]) ??
              DragStartBehavior.start,
          keyboardDismissBehavior:
              ArgumentDecoders.enumValue&amp;lt;ScrollViewKeyboardDismissBehavior&amp;gt;(
                      ScrollViewKeyboardDismissBehavior.values,
                      source,
                      [&apos;keyboardDismissBehavior&apos;]) ??
                  ScrollViewKeyboardDismissBehavior.manual,
          restorationId: source.v&amp;lt;String&amp;gt;([&apos;restorationId&apos;]),
        );
      },
      &apos;Opacity&apos;: (BuildContext context, DataSource source) {
        return AnimatedOpacity(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          opacity: source.v&amp;lt;double&amp;gt;([&apos;opacity&apos;]) ?? 0.0,
          onEnd: source.voidHandler([&apos;onEnd&apos;]),
          alwaysIncludeSemantics:
              source.v&amp;lt;bool&amp;gt;([&apos;alwaysIncludeSemantics&apos;]) ?? true,
        );
      },
      &apos;Padding&apos;: (BuildContext context, DataSource source) {
        return AnimatedPadding(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          padding: ArgumentDecoders.edgeInsets(source, [&apos;padding&apos;]) ??
              EdgeInsets.zero,
          onEnd: source.voidHandler([&apos;onEnd&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;Placeholder&apos;: (BuildContext context, DataSource source) {
        return Placeholder(
          color: ArgumentDecoders.color(source, [&apos;color&apos;]) ??
              const Color(0xFF455A64),
          strokeWidth: source.v&amp;lt;double&amp;gt;([&apos;strokeWidth&apos;]) ?? 2.0,
          fallbackWidth: source.v&amp;lt;double&amp;gt;([&apos;placeholderWidth&apos;]) ?? 400.0,
          fallbackHeight: source.v&amp;lt;double&amp;gt;([&apos;placeholderHeight&apos;]) ?? 400.0,
        );
      },
      &apos;Positioned&apos;: (BuildContext context, DataSource source) {
        return AnimatedPositionedDirectional(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          start: source.v&amp;lt;double&amp;gt;([&apos;start&apos;]),
          top: source.v&amp;lt;double&amp;gt;([&apos;top&apos;]),
          end: source.v&amp;lt;double&amp;gt;([&apos;end&apos;]),
          bottom: source.v&amp;lt;double&amp;gt;([&apos;bottom&apos;]),
          width: source.v&amp;lt;double&amp;gt;([&apos;width&apos;]),
          height: source.v&amp;lt;double&amp;gt;([&apos;height&apos;]),
          onEnd: source.voidHandler([&apos;onEnd&apos;]),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;Rotation&apos;: (BuildContext context, DataSource source) {
        return AnimatedRotation(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          turns: source.v&amp;lt;double&amp;gt;([&apos;turns&apos;]) ?? 0.0,
          alignment: (ArgumentDecoders.alignment(source, [&apos;alignment&apos;]) ??
                  Alignment.center)
              .resolve(Directionality.of(context)),
          filterQuality: ArgumentDecoders.enumValue&amp;lt;FilterQuality&amp;gt;(
              FilterQuality.values, source, [&apos;filterQuality&apos;]),
          onEnd: source.voidHandler([&apos;onEnd&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;Row&apos;: (BuildContext context, DataSource source) {
        return Row(
          mainAxisAlignment: ArgumentDecoders.enumValue&amp;lt;MainAxisAlignment&amp;gt;(
                  MainAxisAlignment.values, source, [&apos;mainAxisAlignment&apos;]) ??
              MainAxisAlignment.start,
          mainAxisSize: ArgumentDecoders.enumValue&amp;lt;MainAxisSize&amp;gt;(
                  MainAxisSize.values, source, [&apos;mainAxisSize&apos;]) ??
              MainAxisSize.max,
          crossAxisAlignment: ArgumentDecoders.enumValue&amp;lt;CrossAxisAlignment&amp;gt;(
                  CrossAxisAlignment.values, source, [&apos;crossAxisAlignment&apos;]) ??
              CrossAxisAlignment.center,
          textDirection: ArgumentDecoders.enumValue&amp;lt;TextDirection&amp;gt;(
              TextDirection.values, source, [&apos;textDirection&apos;]),
          verticalDirection: ArgumentDecoders.enumValue&amp;lt;VerticalDirection&amp;gt;(
                  VerticalDirection.values, source, [&apos;verticalDirection&apos;]) ??
              VerticalDirection.down,
          textBaseline: ArgumentDecoders.enumValue&amp;lt;TextBaseline&amp;gt;(
              TextBaseline.values, source, [&apos;textBaseline&apos;]),
          children: source.childList([&apos;children&apos;]),
        );
      },
      &apos;SafeArea&apos;: (BuildContext context, DataSource source) {
        return SafeArea(
          left: source.v&amp;lt;bool&amp;gt;([&apos;left&apos;]) ?? true,
          top: source.v&amp;lt;bool&amp;gt;([&apos;top&apos;]) ?? true,
          right: source.v&amp;lt;bool&amp;gt;([&apos;right&apos;]) ?? true,
          bottom: source.v&amp;lt;bool&amp;gt;([&apos;bottom&apos;]) ?? true,
          minimum: (ArgumentDecoders.edgeInsets(source, [&apos;minimum&apos;]) ??
                  EdgeInsets.zero)
              .resolve(Directionality.of(context)),
          maintainBottomViewPadding:
              source.v&amp;lt;bool&amp;gt;([&apos;maintainBottomViewPadding&apos;]) ?? false,
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;Scale&apos;: (BuildContext context, DataSource source) {
        return AnimatedScale(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          scale: source.v&amp;lt;double&amp;gt;([&apos;scale&apos;]) ?? 1.0,
          alignment: (ArgumentDecoders.alignment(source, [&apos;alignment&apos;]) ??
                  Alignment.center)
              .resolve(Directionality.of(context)),
          filterQuality: ArgumentDecoders.enumValue&amp;lt;FilterQuality&amp;gt;(
              FilterQuality.values, source, [&apos;filterQuality&apos;]),
          onEnd: source.voidHandler([&apos;onEnd&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;SingleChildScrollView&apos;: (BuildContext context, DataSource source) {
        return SingleChildScrollView(
          scrollDirection: ArgumentDecoders.enumValue&amp;lt;Axis&amp;gt;(
                  Axis.values, source, [&apos;scrollDirection&apos;]) ??
              Axis.vertical,
          reverse: source.v&amp;lt;bool&amp;gt;([&apos;reverse&apos;]) ?? false,
          padding: ArgumentDecoders.edgeInsets(source, [&apos;padding&apos;]),
          primary: source.v&amp;lt;bool&amp;gt;([&apos;primary&apos;]) ?? true,
          dragStartBehavior: ArgumentDecoders.enumValue&amp;lt;DragStartBehavior&amp;gt;(
                  DragStartBehavior.values, source, [&apos;dragStartBehavior&apos;]) ??
              DragStartBehavior.start,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.hardEdge,
          restorationId: source.v&amp;lt;String&amp;gt;([&apos;restorationId&apos;]),
          keyboardDismissBehavior:
              ArgumentDecoders.enumValue&amp;lt;ScrollViewKeyboardDismissBehavior&amp;gt;(
                      ScrollViewKeyboardDismissBehavior.values,
                      source,
                      [&apos;keyboardDismissBehavior&apos;]) ??
                  ScrollViewKeyboardDismissBehavior.manual,
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;SizedBox&apos;: (BuildContext context, DataSource source) {
        return SizedBox(
          width: source.v&amp;lt;double&amp;gt;([&apos;width&apos;]),
          height: source.v&amp;lt;double&amp;gt;([&apos;height&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;SizedBoxExpand&apos;: (BuildContext context, DataSource source) {
        return SizedBox.expand(
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;SizedBoxShrink&apos;: (BuildContext context, DataSource source) {
        return SizedBox.shrink(
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;Spacer&apos;: (BuildContext context, DataSource source) {
        return Spacer(
          flex: source.v&amp;lt;int&amp;gt;([&apos;flex&apos;]) ?? 1,
        );
      },
      &apos;Stack&apos;: (BuildContext context, DataSource source) {
        return Stack(
          alignment: ArgumentDecoders.alignment(source, [&apos;alignment&apos;]) ??
              AlignmentDirectional.topStart,
          textDirection: ArgumentDecoders.enumValue&amp;lt;TextDirection&amp;gt;(
              TextDirection.values, source, [&apos;textDirection&apos;]),
          fit: ArgumentDecoders.enumValue&amp;lt;StackFit&amp;gt;(
                  StackFit.values, source, [&apos;fit&apos;]) ??
              StackFit.loose,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.hardEdge,
          children: source.childList([&apos;children&apos;]),
        );
      },
      &apos;Text&apos;: (BuildContext context, DataSource source) {
        String? text = source.v&amp;lt;String&amp;gt;([&apos;text&apos;]);
        if (text == null) {
          final StringBuffer builder = StringBuffer();
          final int count = source.length([&apos;text&apos;]);
          for (int index = 0; index &amp;lt; count; index += 1) {
            builder.write(source.v&amp;lt;String&amp;gt;([&apos;text&apos;, index]) ?? &apos;&apos;);
          }
          text = builder.toString();
        }
        return Text(
          text,
          style: ArgumentDecoders.textStyle(source, [&apos;style&apos;]),
          strutStyle: ArgumentDecoders.strutStyle(source, [&apos;strutStyle&apos;]),
          textAlign: ArgumentDecoders.enumValue&amp;lt;TextAlign&amp;gt;(
              TextAlign.values, source, [&apos;textAlign&apos;]),
          textDirection: ArgumentDecoders.enumValue&amp;lt;TextDirection&amp;gt;(
              TextDirection.values, source, [&apos;textDirection&apos;]),
          locale: ArgumentDecoders.locale(source, [&apos;locale&apos;]),
          softWrap: source.v&amp;lt;bool&amp;gt;([&apos;softWrap&apos;]),
          overflow: ArgumentDecoders.enumValue&amp;lt;TextOverflow&amp;gt;(
              TextOverflow.values, source, [&apos;overflow&apos;]),
          textScaleFactor: source.v&amp;lt;double&amp;gt;([&apos;textScaleFactor&apos;]),
          maxLines: source.v&amp;lt;int&amp;gt;([&apos;maxLines&apos;]),
          semanticsLabel: source.v&amp;lt;String&amp;gt;([&apos;semanticsLabel&apos;]),
          textWidthBasis: ArgumentDecoders.enumValue&amp;lt;TextWidthBasis&amp;gt;(
              TextWidthBasis.values, source, [&apos;textWidthBasis&apos;]),
          textHeightBehavior: ArgumentDecoders.textHeightBehavior(
              source, [&apos;textHeightBehavior&apos;]),
        );
      },
      &apos;Wrap&apos;: (BuildContext context, DataSource source) {
        return Wrap(
          direction: ArgumentDecoders.enumValue&amp;lt;Axis&amp;gt;(
                  Axis.values, source, [&apos;direction&apos;]) ??
              Axis.horizontal,
          alignment: ArgumentDecoders.enumValue&amp;lt;WrapAlignment&amp;gt;(
                  WrapAlignment.values, source, [&apos;alignment&apos;]) ??
              WrapAlignment.start,
          spacing: source.v&amp;lt;double&amp;gt;([&apos;spacing&apos;]) ?? 0.0,
          runAlignment: ArgumentDecoders.enumValue&amp;lt;WrapAlignment&amp;gt;(
                  WrapAlignment.values, source, [&apos;runAlignment&apos;]) ??
              WrapAlignment.start,
          runSpacing: source.v&amp;lt;double&amp;gt;([&apos;runSpacing&apos;]) ?? 0.0,
          crossAxisAlignment: ArgumentDecoders.enumValue&amp;lt;WrapCrossAlignment&amp;gt;(
                  WrapCrossAlignment.values, source, [&apos;crossAxisAlignment&apos;]) ??
              WrapCrossAlignment.start,
          textDirection: ArgumentDecoders.enumValue&amp;lt;TextDirection&amp;gt;(
              TextDirection.values, source, [&apos;textDirection&apos;]),
          verticalDirection: ArgumentDecoders.enumValue&amp;lt;VerticalDirection&amp;gt;(
                  VerticalDirection.values, source, [&apos;verticalDirection&apos;]) ??
              VerticalDirection.down,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          children: source.childList([&apos;children&apos;]),
        );
      },
    };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This created all the core widgets that are not apart of a design system. Keeping these separate will let you update the design system separately than the core widgets.&lt;/p&gt;
&lt;h3&gt;Defining the Material library&lt;/h3&gt;
&lt;p&gt;Create and update the following file located at &lt;code&gt;app/lib/rfw/material.dart&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/gestures.dart&apos;;
import &apos;package:flutter/material.dart&apos;;
import &apos;package:rfw/rfw.dart&apos;;

import &apos;decoders.dart&apos;;

LocalWidgetLibrary createMaterialWidgets() =&amp;gt;
    LocalWidgetLibrary(_materialWidgetsDefinitions);

Map&amp;lt;String, LocalWidgetBuilder&amp;gt; get _materialWidgetsDefinitions =&amp;gt;
    &amp;lt;String, LocalWidgetBuilder&amp;gt;{
      &apos;AboutListTile&apos;: (context, source) {
        return AboutListTile(
          icon: source.optionalChild([&apos;icon&apos;]),
          applicationName: source.v&amp;lt;String&amp;gt;([&apos;applicationName&apos;]),
          applicationVersion: source.v&amp;lt;String&amp;gt;([&apos;applicationVersion&apos;]),
          applicationIcon: source.optionalChild([&apos;applicationIcon&apos;]),
          applicationLegalese: source.v&amp;lt;String&amp;gt;([&apos;applicationLegalese&apos;]),
          aboutBoxChildren: source.childList([&apos;aboutBoxChildren&apos;]),
          dense: source.v&amp;lt;bool&amp;gt;([&apos;dense&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;AppBar&apos;: (context, source) {
        return AppBar(
          leading: source.optionalChild([&apos;leading&apos;]),
          automaticallyImplyLeading:
              source.v&amp;lt;bool&amp;gt;([&apos;automaticallyImplyLeading&apos;]) ?? true,
          title: source.optionalChild([&apos;title&apos;]),
          actions: source.childList([&apos;actions&apos;]),
          elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
          shadowColor: ArgumentDecoders.color(source, [&apos;shadowColor&apos;]),
          shape: ArgumentDecoders.shapeBorder(source, [&apos;shape&apos;]),
          backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
          foregroundColor: ArgumentDecoders.color(source, [&apos;foregroundColor&apos;]),
          iconTheme: ArgumentDecoders.iconThemeData(source, [&apos;iconTheme&apos;]),
          actionsIconTheme:
              ArgumentDecoders.iconThemeData(source, [&apos;actionsIconTheme&apos;]),
          primary: source.v&amp;lt;bool&amp;gt;([&apos;primary&apos;]) ?? true,
          centerTitle: source.v&amp;lt;bool&amp;gt;([&apos;centerTitle&apos;]),
          excludeHeaderSemantics:
              source.v&amp;lt;bool&amp;gt;([&apos;excludeHeaderSemantics&apos;]) ?? false,
          titleSpacing: source.v&amp;lt;double&amp;gt;([&apos;titleSpacing&apos;]),
          toolbarOpacity: source.v&amp;lt;double&amp;gt;([&apos;toolbarOpacity&apos;]) ?? 1.0,
          toolbarHeight: source.v&amp;lt;double&amp;gt;([&apos;toolbarHeight&apos;]),
          leadingWidth: source.v&amp;lt;double&amp;gt;([&apos;leadingWidth&apos;]),
          toolbarTextStyle:
              ArgumentDecoders.textStyle(source, [&apos;toolbarTextStyle&apos;]),
          titleTextStyle:
              ArgumentDecoders.textStyle(source, [&apos;titleTextStyle&apos;]),
        );
      },
      &apos;ButtonBar&apos;: (context, source) {
        return ButtonBar(
          alignment: ArgumentDecoders.enumValue&amp;lt;MainAxisAlignment&amp;gt;(
                  MainAxisAlignment.values, source, [&apos;alignment&apos;]) ??
              MainAxisAlignment.start,
          mainAxisSize: ArgumentDecoders.enumValue&amp;lt;MainAxisSize&amp;gt;(
                  MainAxisSize.values, source, [&apos;mainAxisSize&apos;]) ??
              MainAxisSize.max,
          buttonMinWidth: source.v&amp;lt;double&amp;gt;([&apos;buttonMinWidth&apos;]),
          buttonHeight: source.v&amp;lt;double&amp;gt;([&apos;buttonHeight&apos;]),
          buttonPadding: ArgumentDecoders.edgeInsets(source, [&apos;buttonPadding&apos;]),
          buttonAlignedDropdown:
              source.v&amp;lt;bool&amp;gt;([&apos;buttonAlignedDropdown&apos;]) ?? false,
          layoutBehavior: ArgumentDecoders.enumValue&amp;lt;ButtonBarLayoutBehavior&amp;gt;(
              ButtonBarLayoutBehavior.values, source, [&apos;layoutBehavior&apos;]),
          overflowDirection: ArgumentDecoders.enumValue&amp;lt;VerticalDirection&amp;gt;(
              VerticalDirection.values, source, [&apos;overflowDirection&apos;]),
          overflowButtonSpacing: source.v&amp;lt;double&amp;gt;([&apos;overflowButtonSpacing&apos;]),
          children: source.childList([&apos;children&apos;]),
        );
      },
      &apos;Card&apos;: (context, source) {
        return Card(
          color: ArgumentDecoders.color(source, [&apos;color&apos;]),
          shadowColor: ArgumentDecoders.color(source, [&apos;shadowColor&apos;]),
          elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
          shape: ArgumentDecoders.shapeBorder(source, [&apos;shape&apos;]),
          borderOnForeground: source.v&amp;lt;bool&amp;gt;([&apos;borderOnForeground&apos;]) ?? true,
          margin: ArgumentDecoders.edgeInsets(source, [&apos;margin&apos;]),
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          semanticContainer: source.v&amp;lt;bool&amp;gt;([&apos;semanticContainer&apos;]) ?? true,
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;CircularProgressIndicator&apos;: (context, source) {
        return CircularProgressIndicator(
          value: source.v&amp;lt;double&amp;gt;([&apos;value&apos;]),
          color: ArgumentDecoders.color(source, [&apos;color&apos;]),
          backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
          strokeWidth: source.v&amp;lt;double&amp;gt;([&apos;strokeWidth&apos;]) ?? 4.0,
          semanticsLabel: source.v&amp;lt;String&amp;gt;([&apos;semanticsLabel&apos;]),
          semanticsValue: source.v&amp;lt;String&amp;gt;([&apos;semanticsValue&apos;]),
        );
      },
      &apos;Divider&apos;: (context, source) {
        return Divider(
          height: source.v&amp;lt;double&amp;gt;([&apos;height&apos;]),
          thickness: source.v&amp;lt;double&amp;gt;([&apos;thickness&apos;]),
          indent: source.v&amp;lt;double&amp;gt;([&apos;indent&apos;]),
          endIndent: source.v&amp;lt;double&amp;gt;([&apos;endIndent&apos;]),
          color: ArgumentDecoders.color(source, [&apos;color&apos;]),
        );
      },
      &apos;Drawer&apos;: (context, source) {
        return Drawer(
          elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]) ?? 16.0,
          semanticLabel: source.v&amp;lt;String&amp;gt;([&apos;semanticLabel&apos;]),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;DrawerHeader&apos;: (context, source) {
        return DrawerHeader(
          duration: ArgumentDecoders.duration(source, [&apos;duration&apos;], context),
          curve: ArgumentDecoders.curve(source, [&apos;curve&apos;], context),
          decoration: ArgumentDecoders.decoration(source, [&apos;decoration&apos;]),
          margin: ArgumentDecoders.edgeInsets(source, [&apos;margin&apos;]) ??
              const EdgeInsets.only(bottom: 8.0),
          padding: ArgumentDecoders.edgeInsets(source, [&apos;padding&apos;]) ??
              const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0),
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;ElevatedButton&apos;: (context, source) {
        return ElevatedButton(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;InkWell&apos;: (context, source) {
        return InkWell(
          onTap: source.voidHandler([&apos;onTap&apos;]),
          onDoubleTap: source.voidHandler([&apos;onDoubleTap&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          onTapDown: source.handler([&apos;onTapDown&apos;],
              (VoidCallback trigger) =&amp;gt; (TapDownDetails details) =&amp;gt; trigger()),
          onTapCancel: source.voidHandler([&apos;onTapCancel&apos;]),
          radius: source.v&amp;lt;double&amp;gt;([&apos;radius&apos;]),
          borderRadius: ArgumentDecoders.borderRadius(source, [&apos;borderRadius&apos;])
              ?.resolve(Directionality.of(context)),
          customBorder: ArgumentDecoders.shapeBorder(source, [&apos;customBorder&apos;]),
          enableFeedback: source.v&amp;lt;bool&amp;gt;([&apos;enableFeedback&apos;]) ?? true,
          excludeFromSemantics:
              source.v&amp;lt;bool&amp;gt;([&apos;excludeFromSemantics&apos;]) ?? false,
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          child: source.optionalChild([&apos;child&apos;]),
        );
      },
      &apos;LinearProgressIndicator&apos;: (context, source) {
        return LinearProgressIndicator(
          value: source.v&amp;lt;double&amp;gt;([&apos;value&apos;]),
          color: ArgumentDecoders.color(source, [&apos;color&apos;]),
          backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
          minHeight: source.v&amp;lt;double&amp;gt;([&apos;minHeight&apos;]),
          semanticsLabel: source.v&amp;lt;String&amp;gt;([&apos;semanticsLabel&apos;]),
          semanticsValue: source.v&amp;lt;String&amp;gt;([&apos;semanticsValue&apos;]),
        );
      },
      &apos;ListTile&apos;: (context, source) {
        return ListTile(
          leading: source.optionalChild([&apos;leading&apos;]),
          title: source.optionalChild([&apos;title&apos;]),
          subtitle: source.optionalChild([&apos;subtitle&apos;]),
          trailing: source.optionalChild([&apos;trailing&apos;]),
          isThreeLine: source.v&amp;lt;bool&amp;gt;([&apos;isThreeLine&apos;]) ?? false,
          dense: source.v&amp;lt;bool&amp;gt;([&apos;dense&apos;]),
          visualDensity:
              ArgumentDecoders.visualDensity(source, [&apos;visualDensity&apos;]),
          shape: ArgumentDecoders.shapeBorder(source, [&apos;shape&apos;]),
          contentPadding:
              ArgumentDecoders.edgeInsets(source, [&apos;contentPadding&apos;]),
          enabled: source.v&amp;lt;bool&amp;gt;([&apos;enabled&apos;]) ?? true,
          onTap: source.voidHandler([&apos;onTap&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          selected: source.v&amp;lt;bool&amp;gt;([&apos;selected&apos;]) ?? false,
          focusColor: ArgumentDecoders.color(source, [&apos;focusColor&apos;]),
          hoverColor: ArgumentDecoders.color(source, [&apos;hoverColor&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          tileColor: ArgumentDecoders.color(source, [&apos;tileColor&apos;]),
          selectedTileColor:
              ArgumentDecoders.color(source, [&apos;selectedTileColor&apos;]),
          enableFeedback: source.v&amp;lt;bool&amp;gt;([&apos;enableFeedback&apos;]),
          horizontalTitleGap: source.v&amp;lt;double&amp;gt;([&apos;horizontalTitleGap&apos;]),
          minVerticalPadding: source.v&amp;lt;double&amp;gt;([&apos;minVerticalPadding&apos;]),
          minLeadingWidth: source.v&amp;lt;double&amp;gt;([&apos;minLeadingWidth&apos;]),
        );
      },
      &apos;Scaffold&apos;: (context, source) {
        final Widget? appBarWidget = source.optionalChild([&apos;appBar&apos;]);
        final List&amp;lt;Widget&amp;gt; persistentFooterButtons =
            source.childList([&apos;persistentFooterButtons&apos;]);
        return Scaffold(
          appBar: appBarWidget == null
              ? null
              : PreferredSize(
                  preferredSize: Size.fromHeight(
                      source.v&amp;lt;double&amp;gt;([&apos;bottomHeight&apos;]) ?? 56.0),
                  child: appBarWidget,
                ),
          body: source.optionalChild([&apos;body&apos;]),
          floatingActionButton: source.optionalChild([&apos;floatingActionButton&apos;]),
          persistentFooterButtons:
              persistentFooterButtons.isEmpty ? null : persistentFooterButtons,
          drawer: source.optionalChild([&apos;drawer&apos;]),
          endDrawer: source.optionalChild([&apos;endDrawer&apos;]),
          bottomNavigationBar: source.optionalChild([&apos;bottomNavigationBar&apos;]),
          bottomSheet: source.optionalChild([&apos;bottomSheet&apos;]),
          backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
          resizeToAvoidBottomInset:
              source.v&amp;lt;bool&amp;gt;([&apos;resizeToAvoidBottomInset&apos;]),
          primary: source.v&amp;lt;bool&amp;gt;([&apos;primary&apos;]) ?? true,
          drawerDragStartBehavior:
              ArgumentDecoders.enumValue&amp;lt;DragStartBehavior&amp;gt;(
                      DragStartBehavior.values,
                      source,
                      [&apos;drawerDragStartBehavior&apos;]) ??
                  DragStartBehavior.start,
          extendBody: source.v&amp;lt;bool&amp;gt;([&apos;extendBody&apos;]) ?? false,
          extendBodyBehindAppBar:
              source.v&amp;lt;bool&amp;gt;([&apos;extendBodyBehindAppBar&apos;]) ?? false,
          drawerScrimColor:
              ArgumentDecoders.color(source, [&apos;drawerScrimColor&apos;]),
          drawerEdgeDragWidth: source.v&amp;lt;double&amp;gt;([&apos;drawerEdgeDragWidth&apos;]),
          drawerEnableOpenDragGesture:
              source.v&amp;lt;bool&amp;gt;([&apos;drawerEnableOpenDragGesture&apos;]) ?? true,
          endDrawerEnableOpenDragGesture:
              source.v&amp;lt;bool&amp;gt;([&apos;endDrawerEnableOpenDragGesture&apos;]) ?? true,
          restorationId: source.v&amp;lt;String&amp;gt;([&apos;restorationId&apos;]),
        );
      },
      &apos;VerticalDivider&apos;: (context, source) {
        return VerticalDivider(
          width: source.v&amp;lt;double&amp;gt;([&apos;width&apos;]),
          thickness: source.v&amp;lt;double&amp;gt;([&apos;thickness&apos;]),
          indent: source.v&amp;lt;double&amp;gt;([&apos;indent&apos;]),
          endIndent: source.v&amp;lt;double&amp;gt;([&apos;endIndent&apos;]),
          color: ArgumentDecoders.color(source, [&apos;color&apos;]),
        );
      },
      &apos;FilledButton&apos;: (context, source) {
        return FilledButton(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          style: CustomArgumentDecoders.filledButtonStyle(
              source, [&apos;style&apos;], context),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;FilledButtonIcon&apos;: (context, source) {
        return FilledButton.icon(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          style: CustomArgumentDecoders.filledButtonStyle(
              source, [&apos;style&apos;], context),
          icon: source.child([&apos;icon&apos;]),
          label: source.child([&apos;label&apos;]),
        );
      },
      &apos;FilledButtonTonal&apos;: (context, source) {
        return FilledButton.tonal(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          style: CustomArgumentDecoders.filledButtonStyle(
              source, [&apos;style&apos;], context),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;FilledButtonTonalIcon&apos;: (context, source) {
        return FilledButton.tonalIcon(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          style: CustomArgumentDecoders.filledButtonStyle(
              source, [&apos;style&apos;], context),
          icon: source.child([&apos;icon&apos;]),
          label: source.child([&apos;label&apos;]),
        );
      },
      &apos;TextButton&apos;: (context, source) {
        return TextButton(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          style: CustomArgumentDecoders.textButtonStyle(
              source, [&apos;style&apos;], context),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;TextButtonIcon&apos;: (context, source) {
        return TextButton.icon(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          style: CustomArgumentDecoders.textButtonStyle(
              source, [&apos;style&apos;], context),
          icon: source.child([&apos;icon&apos;]),
          label: source.child([&apos;label&apos;]),
        );
      },
      &apos;OutlinedButton&apos;: (context, source) {
        return OutlinedButton(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          style: CustomArgumentDecoders.outlinedButtonStyle(
              source, [&apos;style&apos;], context),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;OutlinedButtonIcon&apos;: (context, source) {
        return OutlinedButton.icon(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          onLongPress: source.voidHandler([&apos;onLongPress&apos;]),
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          style: CustomArgumentDecoders.outlinedButtonStyle(
              source, [&apos;style&apos;], context),
          icon: source.child([&apos;icon&apos;]),
          label: source.child([&apos;label&apos;]),
        );
      },
      &apos;FloatingActionButton&apos;: (context, source) {
        return FloatingActionButton(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          tooltip: source.v&amp;lt;String&amp;gt;([&apos;tooltip&apos;]),
          foregroundColor: ArgumentDecoders.color(source, [&apos;foregroundColor&apos;]),
          backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
          focusColor: ArgumentDecoders.color(source, [&apos;focusColor&apos;]),
          hoverColor: ArgumentDecoders.color(source, [&apos;hoverColor&apos;]),
          splashColor: ArgumentDecoders.color(source, [&apos;splashColor&apos;]),
          heroTag: source.v&amp;lt;String&amp;gt;([&apos;heroTag&apos;]),
          elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
          focusElevation: source.v&amp;lt;double&amp;gt;([&apos;focusElevation&apos;]),
          hoverElevation: source.v&amp;lt;double&amp;gt;([&apos;hoverElevation&apos;]),
          highlightElevation: source.v&amp;lt;double&amp;gt;([&apos;highlightElevation&apos;]),
          disabledElevation: source.v&amp;lt;double&amp;gt;([&apos;disabledElevation&apos;]),
          mini: source.v&amp;lt;bool&amp;gt;([&apos;mini&apos;]) ?? false,
          shape: ArgumentDecoders.shapeBorder(source, [&apos;shape&apos;]),
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          mouseCursor:
              CustomArgumentDecoders.mouseCursor(source, [&apos;mouseCursor&apos;]),
          materialTapTargetSize:
              ArgumentDecoders.enumValue&amp;lt;MaterialTapTargetSize&amp;gt;(
                  MaterialTapTargetSize.values,
                  source,
                  [&apos;materialTapTargetSize&apos;]),
          isExtended: source.v&amp;lt;bool&amp;gt;([&apos;isExtended&apos;]) ?? false,
          enableFeedback: source.v&amp;lt;bool&amp;gt;([&apos;enableFeedback&apos;]),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;FloatingActionButtonExtended&apos;: (context, source) {
        return FloatingActionButton.extended(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          tooltip: source.v&amp;lt;String&amp;gt;([&apos;tooltip&apos;]),
          foregroundColor: ArgumentDecoders.color(source, [&apos;foregroundColor&apos;]),
          backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
          focusColor: ArgumentDecoders.color(source, [&apos;focusColor&apos;]),
          hoverColor: ArgumentDecoders.color(source, [&apos;hoverColor&apos;]),
          splashColor: ArgumentDecoders.color(source, [&apos;splashColor&apos;]),
          heroTag: source.v&amp;lt;String&amp;gt;([&apos;heroTag&apos;]),
          elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
          focusElevation: source.v&amp;lt;double&amp;gt;([&apos;focusElevation&apos;]),
          hoverElevation: source.v&amp;lt;double&amp;gt;([&apos;hoverElevation&apos;]),
          highlightElevation: source.v&amp;lt;double&amp;gt;([&apos;highlightElevation&apos;]),
          disabledElevation: source.v&amp;lt;double&amp;gt;([&apos;disabledElevation&apos;]),
          shape: ArgumentDecoders.shapeBorder(source, [&apos;shape&apos;]),
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          mouseCursor:
              CustomArgumentDecoders.mouseCursor(source, [&apos;mouseCursor&apos;]),
          materialTapTargetSize:
              ArgumentDecoders.enumValue&amp;lt;MaterialTapTargetSize&amp;gt;(
                  MaterialTapTargetSize.values,
                  source,
                  [&apos;materialTapTargetSize&apos;]),
          isExtended: source.v&amp;lt;bool&amp;gt;([&apos;isExtended&apos;]) ?? true,
          enableFeedback: source.v&amp;lt;bool&amp;gt;([&apos;enableFeedback&apos;]),
          label: source.child([&apos;label&apos;]),
          icon: source.child([&apos;icon&apos;]),
        );
      },
      &apos;FloatingActionButtonSmall&apos;: (context, source) {
        return FloatingActionButton.small(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          tooltip: source.v&amp;lt;String&amp;gt;([&apos;tooltip&apos;]),
          foregroundColor: ArgumentDecoders.color(source, [&apos;foregroundColor&apos;]),
          backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
          focusColor: ArgumentDecoders.color(source, [&apos;focusColor&apos;]),
          hoverColor: ArgumentDecoders.color(source, [&apos;hoverColor&apos;]),
          splashColor: ArgumentDecoders.color(source, [&apos;splashColor&apos;]),
          heroTag: source.v&amp;lt;String&amp;gt;([&apos;heroTag&apos;]),
          elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
          focusElevation: source.v&amp;lt;double&amp;gt;([&apos;focusElevation&apos;]),
          hoverElevation: source.v&amp;lt;double&amp;gt;([&apos;hoverElevation&apos;]),
          highlightElevation: source.v&amp;lt;double&amp;gt;([&apos;highlightElevation&apos;]),
          disabledElevation: source.v&amp;lt;double&amp;gt;([&apos;disabledElevation&apos;]),
          shape: ArgumentDecoders.shapeBorder(source, [&apos;shape&apos;]),
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          mouseCursor:
              CustomArgumentDecoders.mouseCursor(source, [&apos;mouseCursor&apos;]),
          materialTapTargetSize:
              ArgumentDecoders.enumValue&amp;lt;MaterialTapTargetSize&amp;gt;(
                  MaterialTapTargetSize.values,
                  source,
                  [&apos;materialTapTargetSize&apos;]),
          enableFeedback: source.v&amp;lt;bool&amp;gt;([&apos;enableFeedback&apos;]),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;FloatingActionButtonLarge&apos;: (context, source) {
        return FloatingActionButton.large(
          onPressed: source.voidHandler([&apos;onPressed&apos;]),
          tooltip: source.v&amp;lt;String&amp;gt;([&apos;tooltip&apos;]),
          foregroundColor: ArgumentDecoders.color(source, [&apos;foregroundColor&apos;]),
          backgroundColor: ArgumentDecoders.color(source, [&apos;backgroundColor&apos;]),
          focusColor: ArgumentDecoders.color(source, [&apos;focusColor&apos;]),
          hoverColor: ArgumentDecoders.color(source, [&apos;hoverColor&apos;]),
          splashColor: ArgumentDecoders.color(source, [&apos;splashColor&apos;]),
          heroTag: source.v&amp;lt;String&amp;gt;([&apos;heroTag&apos;]),
          elevation: source.v&amp;lt;double&amp;gt;([&apos;elevation&apos;]),
          focusElevation: source.v&amp;lt;double&amp;gt;([&apos;focusElevation&apos;]),
          hoverElevation: source.v&amp;lt;double&amp;gt;([&apos;hoverElevation&apos;]),
          highlightElevation: source.v&amp;lt;double&amp;gt;([&apos;highlightElevation&apos;]),
          disabledElevation: source.v&amp;lt;double&amp;gt;([&apos;disabledElevation&apos;]),
          shape: ArgumentDecoders.shapeBorder(source, [&apos;shape&apos;]),
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          autofocus: source.v&amp;lt;bool&amp;gt;([&apos;autofocus&apos;]) ?? false,
          mouseCursor:
              CustomArgumentDecoders.mouseCursor(source, [&apos;mouseCursor&apos;]),
          materialTapTargetSize:
              ArgumentDecoders.enumValue&amp;lt;MaterialTapTargetSize&amp;gt;(
                  MaterialTapTargetSize.values,
                  source,
                  [&apos;materialTapTargetSize&apos;]),
          enableFeedback: source.v&amp;lt;bool&amp;gt;([&apos;enableFeedback&apos;]),
          child: source.child([&apos;child&apos;]),
        );
      },
      &apos;InteractiveViewer&apos;: (context, source) {
        return InteractiveViewer(
          clipBehavior: ArgumentDecoders.enumValue&amp;lt;Clip&amp;gt;(
                  Clip.values, source, [&apos;clipBehavior&apos;]) ??
              Clip.none,
          alignPanAxis: source.v&amp;lt;bool&amp;gt;([&apos;alignPanAxis&apos;]) ?? false,
          panAxis: ArgumentDecoders.enumValue&amp;lt;PanAxis&amp;gt;(
                  PanAxis.values, source, [&apos;panAxis&apos;]) ??
              PanAxis.free,
          boundaryMargin:
              CustomArgumentDecoders.edgeInsets(source, [&apos;boundaryMargin&apos;]) ??
                  EdgeInsets.zero,
          constrained: source.v&amp;lt;bool&amp;gt;([&apos;constrained&apos;]) ?? true,
          maxScale: source.v&amp;lt;double&amp;gt;([&apos;maxScale&apos;]) ?? 2.5,
          minScale: source.v&amp;lt;double&amp;gt;([&apos;minScale&apos;]) ?? 0.8,
          interactionEndFrictionCoefficient:
              source.v&amp;lt;double&amp;gt;([&apos;interactionEndFrictionCoefficient&apos;]) ??
                  0.0000135,
          panEnabled: source.v&amp;lt;bool&amp;gt;([&apos;panEnabled&apos;]) ?? true,
          scaleEnabled: source.v&amp;lt;bool&amp;gt;([&apos;scaleEnabled&apos;]) ?? true,
          scaleFactor: source.v&amp;lt;double&amp;gt;([&apos;scaleFactor&apos;]) ?? 0.8,
          alignment:
              ArgumentDecoders.alignment(source, [&apos;alignment&apos;]) as Alignment?,
          trackpadScrollCausesScale:
              source.v&amp;lt;bool&amp;gt;([&apos;trackpadScrollCausesScale&apos;]) ?? false,
          child: source.child([&apos;child&apos;]),
        );
      },
    };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example we are using the &lt;a href=&quot;https://m3.material.io/&quot;&gt;Material&lt;/a&gt; widgets but this could be the &lt;a href=&quot;https://pub.dev/packages/fluent_ui&quot;&gt;fluent_ui&lt;/a&gt; or &lt;a href=&quot;https://pub.dev/packages/macos_ui&quot;&gt;macos_ui&lt;/a&gt; package set of components or even your custom design system.&lt;/p&gt;
&lt;p&gt;Now that we have defined the core of rfw we can start to add the logic and UI for our app.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The rfw package includes material and core directly so you can simply import it directly and not need to define like this.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Connecting to the server&lt;/h3&gt;
&lt;p&gt;Create and update the following file located at &lt;code&gt;app/lib/main.dart&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;network.dart&apos;;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter SSR Example&apos;,
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const NetworkExample(),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next create and update the following file located at &lt;code&gt;app/lib/network.dart&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:http/http.dart&apos; as http;

import &apos;package:rfw/rfw.dart&apos;;

import &apos;rfw/material.dart&apos; as m;
import &apos;rfw/core.dart&apos; as c;

class NetworkExample extends StatefulWidget {
  const NetworkExample({super.key});

  @override
  State&amp;lt;NetworkExample&amp;gt; createState() =&amp;gt; _NetworkExampleState();
}

class _NetworkExampleState extends State&amp;lt;NetworkExample&amp;gt; {
  final Runtime _runtime = Runtime();
  final DynamicContent _data = DynamicContent();
  bool loaded = false;
  int count = 0;
  final route = Uri.parse(&apos;http://localhost:8080/&apos;);

  @override
  void initState() {
    super.initState();
    _update();
  }

  @override
  void reassemble() {
    super.reassemble();
    _update();
  }

  static const coreName = LibraryName([&apos;widgets&apos;]);
  static const materialName = LibraryName([&apos;material&apos;]);
  static const remoteName = LibraryName([&apos;remote&apos;]);

  void _update() async {
    _runtime.update(coreName, c.createCoreWidgets());
    _runtime.update(materialName, m.createMaterialWidgets());
    await fetchWidget();
    if (mounted) setState(() =&amp;gt; loaded = true);
  }

  Future&amp;lt;void&amp;gt; fetchWidget() async {
    final res = await http.get(route, headers: {
      &apos;COUNTER_VALUE&apos;: count.toString(),
    });
    if (res.statusCode == 200) {
      count = int.tryParse(res.headers[&apos;counter_value&apos;].toString()) ?? count;
      _data.update(&apos;counter&apos;, &amp;lt;String, Object&amp;gt;{&apos;value&apos;: &apos;$count&apos;});
      _runtime.update(remoteName, decodeLibraryBlob(res.bodyBytes));
    }
  }

  void onEvent(String name, DynamicMap arguments) async {
    debugPrint(&apos;user triggered event &amp;quot;$name&amp;quot; with data: $arguments&apos;);
    if (name == &apos;click&apos;) {
      final res = await http.post(route, headers: {
        &apos;COUNTER_VALUE&apos;: count.toString(),
      });
      if (res.statusCode == 200) {
        count = int.tryParse(res.headers[&apos;counter_value&apos;].toString()) ?? count;
        _data.update(&apos;counter&apos;, &amp;lt;String, Object&amp;gt;{&apos;value&apos;: &apos;$count&apos;});
        _runtime.update(remoteName, decodeLibraryBlob(res.bodyBytes));
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    final colors = Theme.of(context).colorScheme;
    _data.update(&apos;colorScheme&apos;, &amp;lt;String, Object&amp;gt;{
      &apos;inversePrimary&apos;: colors.inversePrimary.value,
      &apos;inverseSurface&apos;: colors.inverseSurface.value,
      &apos;onInverseSurface&apos;: colors.onInverseSurface.value,
      &apos;primary&apos;: colors.primary.value,
      &apos;onPrimary&apos;: colors.onPrimary.value,
      &apos;primaryContainer&apos;: colors.primaryContainer.value,
      &apos;onPrimaryContainer&apos;: colors.onPrimaryContainer.value,
      &apos;secondary&apos;: colors.secondary.value,
      &apos;onSecondary&apos;: colors.onSecondary.value,
      &apos;secondaryContainer&apos;: colors.secondaryContainer.value,
      &apos;onSecondaryContainer&apos;: colors.onSecondaryContainer.value,
      &apos;tertiary&apos;: colors.tertiary.value,
      &apos;onTertiary&apos;: colors.onTertiary.value,
      &apos;tertiaryContainer&apos;: colors.tertiaryContainer.value,
      &apos;onTertiaryContainer&apos;: colors.onTertiaryContainer.value,
      &apos;error&apos;: colors.error.value,
      &apos;onError&apos;: colors.onError.value,
      &apos;errorContainer&apos;: colors.errorContainer.value,
      &apos;onErrorContainer&apos;: colors.onErrorContainer.value,
      &apos;background&apos;: colors.background.value,
      &apos;onBackground&apos;: colors.onBackground.value,
      &apos;surface&apos;: colors.surface.value,
      &apos;onSurface&apos;: colors.onSurface.value,
      &apos;outline&apos;: colors.outline.value,
      &apos;outlineVariant&apos;: colors.outlineVariant.value,
      &apos;scrim&apos;: colors.scrim.value,
      &apos;shadow&apos;: colors.shadow.value,
    });
    if (!loaded) {
      return const Center(child: CircularProgressIndicator());
    }
    const root = FullyQualifiedWidgetName(remoteName, &apos;root&apos;);
    return Container(
      color: Theme.of(context).scaffoldBackgroundColor,
      child: RemoteWidget(
        runtime: _runtime,
        data: _data,
        widget: root,
        onEvent: onEvent,
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is a lot going on here but simply we are connecting to our server and defining the local widgets we created and creating a local counter state that is used to send to the server and update per the response.&lt;/p&gt;
&lt;p&gt;Because the &lt;code&gt;DynamicContent _data&lt;/code&gt; can be updated every frame we are setting the local colors from the apps current theme int he build method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;_data.update(&apos;colorScheme&apos;, &amp;lt;String, Object&amp;gt;{
      &apos;inversePrimary&apos;: colors.inversePrimary.value,
      &apos;inverseSurface&apos;: colors.inverseSurface.value,
      &apos;onInverseSurface&apos;: colors.onInverseSurface.value,
      &apos;primary&apos;: colors.primary.value,
      &apos;onPrimary&apos;: colors.onPrimary.value,
      &apos;primaryContainer&apos;: colors.primaryContainer.value,
      &apos;onPrimaryContainer&apos;: colors.onPrimaryContainer.value,
      &apos;secondary&apos;: colors.secondary.value,
      &apos;onSecondary&apos;: colors.onSecondary.value,
      &apos;secondaryContainer&apos;: colors.secondaryContainer.value,
      &apos;onSecondaryContainer&apos;: colors.onSecondaryContainer.value,
      &apos;tertiary&apos;: colors.tertiary.value,
      &apos;onTertiary&apos;: colors.onTertiary.value,
      &apos;tertiaryContainer&apos;: colors.tertiaryContainer.value,
      &apos;onTertiaryContainer&apos;: colors.onTertiaryContainer.value,
      &apos;error&apos;: colors.error.value,
      &apos;onError&apos;: colors.onError.value,
      &apos;errorContainer&apos;: colors.errorContainer.value,
      &apos;onErrorContainer&apos;: colors.onErrorContainer.value,
      &apos;background&apos;: colors.background.value,
      &apos;onBackground&apos;: colors.onBackground.value,
      &apos;surface&apos;: colors.surface.value,
      &apos;onSurface&apos;: colors.onSurface.value,
      &apos;outline&apos;: colors.outline.value,
      &apos;outlineVariant&apos;: colors.outlineVariant.value,
      &apos;scrim&apos;: colors.scrim.value,
      &apos;shadow&apos;: colors.shadow.value,
    });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we first load the UI we want to make a &lt;code&gt;GET&lt;/code&gt; request to get the latest from the server or fallback to the latest in cache (or even &lt;code&gt;rootBundle&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;Future&amp;lt;void&amp;gt; fetchWidget() async {
    final res = await http.get(route, headers: {
      &apos;COUNTER_VALUE&apos;: count.toString(),
    });
    if (res.statusCode == 200) {
      count = int.tryParse(res.headers[&apos;counter_value&apos;].toString()) ?? count;
      _data.update(&apos;counter&apos;, &amp;lt;String, Object&amp;gt;{&apos;value&apos;: &apos;$count&apos;});
      _runtime.update(remoteName, decodeLibraryBlob(res.bodyBytes));
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting and reading the headers will allow us to send state to the server and respond on updates.&lt;/p&gt;
&lt;p&gt;We can also respond to events in the UI and trigger requests:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;void onEvent(String name, DynamicMap arguments) async {
    debugPrint(&apos;user triggered event &amp;quot;$name&amp;quot; with data: $arguments&apos;);
    if (name == &apos;click&apos;) {
      final res = await http.post(route, headers: {
        &apos;COUNTER_VALUE&apos;: count.toString(),
      });
      if (res.statusCode == 200) {
        count = int.tryParse(res.headers[&apos;counter_value&apos;].toString()) ?? count;
        _data.update(&apos;counter&apos;, &amp;lt;String, Object&amp;gt;{&apos;value&apos;: &apos;$count&apos;});
        _runtime.update(remoteName, decodeLibraryBlob(res.bodyBytes));
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are using the response of the &lt;code&gt;POST&lt;/code&gt; request to update the UI but we could also call the &lt;code&gt;fetchWidget&lt;/code&gt; method again to get the latest and use the headers to update the data.&lt;/p&gt;
&lt;p&gt;To run the application simply run using &lt;code&gt;flutter run&lt;/code&gt; and make sure if you use MacOS desktop target to set the correct network permissions.&lt;/p&gt;
&lt;p&gt;If all goes well you should see the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/r_1_phzhlfqsnj.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This will trigger network requests for each button press and the UI will reflect the logic done on the server.&lt;/p&gt;
&lt;p&gt;You could also call a database and update the UI based on the response.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There is a lot more we can do with this example but after doing a deep dive on the format I thought it would be useful for others to understand and see some examples.&lt;/p&gt;
&lt;p&gt;The final code can be found &lt;a href=&quot;https://github.com/rodydavis/flutter_ssr&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have any questions reach out to me on &lt;a href=&quot;https://twitter.com/rodydavis&quot;&gt;Twitter&lt;/a&gt; or &lt;a href=&quot;https://github.com/rodydavis&quot;&gt;Github&lt;/a&gt;!&lt;/p&gt;
</content:encoded></item><item><title>Deep Linking for Flutter Web</title><link>https://rodydavis.com/flutter/web-deep-linking/</link><guid isPermaLink="true">https://rodydavis.com/flutter/web-deep-linking/</guid><description>Learn how to implement deep linking for Flutter Web to enable proper URL navigation, including opening links to specific pages and protected routes.</description><pubDate>Sun, 19 Jan 2025 07:56:03 GMT</pubDate><content:encoded>&lt;h1&gt;Deep Linking for Flutter Web&lt;/h1&gt;
&lt;p&gt;In this article I will show you how to have proper URL navigation for your application. Open links to specific pages, protected routes and custom transitions.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/flutter_deep_linking&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/flutter_deep_linking/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;Create a new flutter project called “flutter_deep_linking”&lt;/p&gt;
&lt;p&gt;Open that folder up in VSCode.&lt;/p&gt;
&lt;p&gt;Update your “pubspec.yaml” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_1_qbot26kikl.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Step 1&lt;/h2&gt;
&lt;p&gt;Create a file at “lib/ui/home/screen.dart” and add the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_2_8o7bk1h6ep.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Update your “lib/main.dart” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_3_u638b3ccuy.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Run your application and you should see the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_4_fk5zfnixom.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Step 2&lt;/h2&gt;
&lt;p&gt;Now we need to grab the url the user enters into the address bar.&lt;/p&gt;
&lt;p&gt;Create a folder at this location “lib/plugins/navigator”&lt;/p&gt;
&lt;p&gt;Create a file inside named: “web.dart” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_5_r37mgvwx5q.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create a file inside named: “unsupported.dart” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_6_xogk4ly3ze.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create a file inside named: “navigator.dart” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_7_atfvh3z2tt.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now go back to your “lib/main.dart” file and add the navigator:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_8_4esmqtror6.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s important to import the navigator as shown as this will have the conditional import for web compiling.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you run the app now nothing should change.&lt;/p&gt;
&lt;h2&gt;Step 3&lt;/h2&gt;
&lt;p&gt;Now let’s add the proper routing.&lt;/p&gt;
&lt;p&gt;Create a new file “lib/ui/router.dart” and add the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_9_xae1ebw89x.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Also update “lib/main.dart” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_10_ijxwpy9f4h.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice how we removed the “home” field for MaterialApp. This is because the router will handle everything. By default we will go home on “/”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Step 4&lt;/h2&gt;
&lt;p&gt;Now let’s add multiple screens to put this to the test! Add the following folders and files.&lt;/p&gt;
&lt;p&gt;Create a file “lib/ui/account/screen.dart” and add the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_11_o9tev9myvc.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create a file “lib/ui/settings/screen.dart” and add the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_12_8gbj5hxdmq.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create a file “lib/ui/about/screen.dart” and add the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_13_aojf2nh3fj.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add the following to “lib/ui/router.dart”:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_14_9p3q4k5nqt.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now when you navigate to /about, /account and /settings you will go to the new pages!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_15_lahb8r2wyw.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Step 5&lt;/h2&gt;
&lt;p&gt;Now let’s tie into the browser navigation buttons! Update “lib/ui/home/screen.dart” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_16_6ccpd3agpc.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now when you run the application and click on the settings icon it will launch the new screen as expected. But if you click your browsers back button it will go back to the home screen!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_17_rovfeoa2t1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_18_75rxz9598q.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Step 6&lt;/h2&gt;
&lt;p&gt;These urls are great but what if you want to pass data such as an ID that is not known ahead of time? No worries!&lt;/p&gt;
&lt;p&gt;Update “lib/ui/account/screen.dart” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_19_qyhv82wxfe.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Let’s update our “lib/ui/router.dart” with the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_20_i6mmwwv4bg.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now when you run your application and navigate to “/account/40” you will see the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/deep_21_ytsnzw349i.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Dynamic routes work great for Flutter web, you just need to know what to tweak! This package uses a forked version of fluro for some fixes I added but once the PRs is merged you can just use the regular package. Let me know what you think below and if there is a better way I am not seeing!&lt;/p&gt;
&lt;p&gt;Here is the final code: &lt;a href=&quot;https://github.com/rodydavis/flutter_deep_linking&quot;&gt;https://github.com/rodydavis/flutter_deep_linking&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>How To Send Push Notifications on Flutter Web (FCM)</title><link>https://rodydavis.com/flutter/web-push-notifications/</link><guid isPermaLink="true">https://rodydavis.com/flutter/web-push-notifications/</guid><description>*Learning how to implement Firebase Cloud Messaging (FCM) for sending push notifications specifically within a Flutter Web application.*</description><pubDate>Mon, 20 Jan 2025 01:11:12 GMT</pubDate><content:encoded>&lt;h1&gt;How To Send Push Notifications on Flutter Web (FCM)&lt;/h1&gt;
&lt;p&gt;If you are using Firebase then you are probably familiar with Firebase Cloud Messaging. The setup on Flutter web is very different than mobile and other plugins you are probably used to.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/p_1_sml0ik8i1v.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Setting Up&lt;/h2&gt;
&lt;p&gt;Open your web/index.html and look for the following script. If you do not have one you can add it now in the tag.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;script&amp;gt;
if (&amp;quot;serviceWorker&amp;quot; in navigator) {
  window.addEventListener(&amp;quot;load&amp;quot;, function () {
    navigator.serviceWorker.register(&amp;quot;/flutter_service_worker.js&amp;quot;);
  });
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to modify it to support the FCM service worker. The important thing we need to do is comment out the flutter_service_worker.js so that we will not get 404 errors when registering the FCM service worker.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;script&amp;gt;
if (&amp;quot;serviceWorker&amp;quot; in navigator) {
  window.addEventListener(&amp;quot;load&amp;quot;, function () {
    // navigator.serviceWorker.register(&amp;quot;/flutter_service_worker.js&amp;quot;);
    navigator.serviceWorker.register(&amp;quot;/firebase-messaging-sw.js&amp;quot;);
  });
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create a new file called firebase-messaging-sw.js in the web folder with the following contents:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;importScripts(&amp;quot;https://www.gstatic.com/firebasejs/7.5.0/firebase-app.js&amp;quot;);
importScripts(&amp;quot;https://www.gstatic.com/firebasejs/7.5.0/firebase-messaging.js&amp;quot;);
firebase.initializeApp({
    apiKey: &amp;quot;API_KEY&amp;quot;,
    authDomain: &amp;quot;AUTH_DOMAIN&amp;quot;,
    databaseURL: &amp;quot;DATABASE_URL&amp;quot;,
    projectId: &amp;quot;PROJECT_ID&amp;quot;,
    storageBucket: &amp;quot;STORAGE_BUCKET&amp;quot;,
    messagingSenderId: &amp;quot;MESSAGING_SENDER_ID&amp;quot;,
    appId: &amp;quot;APP_ID&amp;quot;,
    measurementId: &amp;quot;MEASUREMENT_ID&amp;quot;
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
    const promiseChain = clients
        .matchAll({
            type: &amp;quot;window&amp;quot;,
            includeUncontrolled: true
        })
        .then(windowClients =&amp;gt; {
            for (let i = 0; i &amp;lt; windowClients.length; i++) {
                const windowClient = windowClients[i];
                windowClient.postMessage(payload);
            }
        })
        .then(() =&amp;gt; {
            return registration.showNotification(&amp;quot;New Message&amp;quot;);
        });
    return promiseChain;
});
self.addEventListener(&apos;notificationclick&apos;, function (event) {
    console.log(&apos;notification received: &apos;, event)
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to replace the config keys with your firebase keys.&lt;/p&gt;
&lt;h2&gt;Helper Methods&lt;/h2&gt;
&lt;p&gt;Create a new dart file wherever you like named firebase_messaging.dart with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;
import &apos;package:firebase/firebase.dart&apos; as firebase;

class FBMessaging {
  FBMessaging._();
  static FBMessaging _instance = FBMessaging._();
  static FBMessaging get instance =&amp;gt; _instance;
  firebase.Messaging _mc;
  String _token;

  final _controller = StreamController&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt;.broadcast();
  Stream&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt; get stream =&amp;gt; _controller.stream;

  void close() {
    _controller?.close();
  }

  Future&amp;lt;void&amp;gt; init() async {
    _mc = firebase.messaging();
    _mc.usePublicVapidKey(&apos;FCM_SERVER_KEY&apos;);
    _mc.onMessage.listen((event) {
      _controller.add(event?.data);
    });
  }

  Future requestPermission() {
    return _mc.requestPermission();
  }

  Future&amp;lt;String&amp;gt; getToken([bool force = false]) async {
    if (force || _token == null) {
      await requestPermission();
      _token = await _mc.getToken();
    }
    return _token;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a button in the app that will be used to request permissions. While it is possible to request for permission when the app launches this is usually bad practice as the user is unlikely to accept and there is no trust built yet. You can request permissions with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final _messaging = FBMessaging.instance;
_messaging.requestPermission().then((_) async {
  final _token = await _messaging.getToken();
  print(&apos;Token: $_token&apos;);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can listen to messages with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;final _messaging = FBMessaging.instance;
_messaging.stream.listen((event) {
  print(&apos;New Message: ${event}&apos;);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Testing&lt;/h2&gt;
&lt;p&gt;Now when you run your application and request permissions you will get a token back. With this token you can open the firebase console and sent a test message to the token.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/p_2_cab21gsn50.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you can send push notifications to Flutter apps! You still need to use conditional imports to support the mobile side as well but stay tuned for an example with that. Let me know your questions and any feedback you may have.&lt;/p&gt;
</content:encoded></item><item><title>How to build a Flutter app on Xcode Cloud</title><link>https://rodydavis.com/flutter/xcode-cloud/</link><guid isPermaLink="true">https://rodydavis.com/flutter/xcode-cloud/</guid><description>Learn how to set up Xcode Cloud to build and distribute Flutter applications for TestFlight and the App Store.</description><pubDate>Mon, 20 Jan 2025 00:38:40 GMT</pubDate><content:encoded>&lt;h1&gt;How to build a Flutter app on Xcode Cloud&lt;/h1&gt;
&lt;p&gt;In this article we are going to go over how to setup &lt;a href=&quot;https://developer.apple.com/xcode-cloud/&quot;&gt;Xcode Cloud&lt;/a&gt; to build your &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; application for &lt;a href=&quot;https://developer.apple.com/testflight/&quot;&gt;TestFlight&lt;/a&gt; and the &lt;a href=&quot;https://developer.apple.com/app-store/&quot;&gt;AppStore&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Step 1&lt;/h2&gt;
&lt;p&gt;Before we begin Flutter needs to be installed, and you can check by running the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;flutter doctor -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After it is installed we can run the following command to create and open our Flutter project (skip down to step 2 if adding to an existing app).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir flutter_ci_example
cd flutter_ci_example
flutter create .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need more help with creating the first project you can check out my previous blog post &lt;a href=&quot;https://rodydavis.com/posts/first-flutter-project/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After the project is created open it in your favorite code editor.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;code .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2&lt;/h2&gt;
&lt;p&gt;The generated files should look like the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/x_1_qw8btmibvc.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create a new file at &lt;code&gt;ios/ci_scripts/ci_post_install.sh&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;#!/bin/sh

# Install CocoaPods using Homebrew.
brew install cocoapods

# Install Flutter
brew install --cask flutter

# Run Flutter doctor
flutter doctor

# Get packages
flutter packages get

# Update generated files
flutter pub run build_runner build

# Build ios app
flutter build ios --no-codesign
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a file Xcode Cloud needs to run after the project is downloaded. We need to install &lt;a href=&quot;https://cocoapods.org/&quot;&gt;cocoapods&lt;/a&gt; for any plugins we are using and Flutter to prebuild our application.&lt;/p&gt;
&lt;p&gt;Then run the following command which will make the script executable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;chmod +x ios/ci_scripts/ci_post_clone.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 3&lt;/h2&gt;
&lt;p&gt;Open up the iOS project in Xcode by right clicking on the iOS folder and selecting &amp;quot;Open in Xcode&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/x_2_i34bfwz1u0.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can also open the project by double clicking on the &lt;code&gt;ios/Runner.xcworkspace&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/x_3_80qu3qrdlp.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Make sure you have the latest version of Xcode Cloud install and that you have &lt;a href=&quot;https://developer.apple.com/xcode-cloud/beta/&quot;&gt;access to the beta&lt;/a&gt;. Create a new workflow by the menu &lt;code&gt;Product &amp;gt; Xcode Cloud &amp;gt; Create Workflow&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/x_4_xsrdzbddjh.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Follow the flow to add the project and choose which type of build you want.&lt;/p&gt;
&lt;p&gt;Make sure to remove MacOS as a target in the workflow by selecting &lt;code&gt;Archive - MacOS&lt;/code&gt; and the delete icon on the top right.&lt;/p&gt;
&lt;p&gt;If you want to build and release the MacOS app you will need to do that with another script in the macos folder and a workflow in that Xcode workspace.&lt;/p&gt;
&lt;p&gt;You can create the file &lt;code&gt;macos/ci_scripts/ci_post_clone.sh&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;#!/bin/sh

# Install CocoaPods using Homebrew.
brew install cocoapods

# Install Flutter
brew install --cask flutter

# Run Flutter doctor
flutter doctor

# Enable macos
flutter config --enable-macos-desktop

# Get packages
flutter packages get

# Update generated files
flutter pub run build_runner build

# Build ios app
flutter build ios --no-codesign
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If all goes well it will look like the following after a successful build:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/x_5_zgz4x31cbp.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Flutter makes it ease to build and deploy to multiple platforms and Xcode Cloud takes care of the signing for Apple platforms.&lt;/p&gt;
&lt;p&gt;You can learn more about cd and flutter &lt;a href=&quot;https://docs.flutter.dev/deployment/cd&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Dynamic Themes with CodeMirror</title><link>https://rodydavis.com/lit/codemirror-dynamic-theme/</link><guid isPermaLink="true">https://rodydavis.com/lit/codemirror-dynamic-theme/</guid><description>This tutorial guides users on how to create a dynamic, themed code editor by integrating CodeMirror within a Lit web component and applying Material Design theming.</description><pubDate>Sun, 19 Jan 2025 23:52:54 GMT</pubDate><content:encoded>&lt;h1&gt;Dynamic Themes with CodeMirror&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to create a a code window that uses &lt;a href=&quot;https://codemirror.net/&quot;&gt;CodeMirror&lt;/a&gt; and apply a dynamic theme with &lt;a href=&quot;https://material.io/&quot;&gt;Material Design&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/codemirror-dynamic-theme&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/codemirror-dynamic-theme/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;codemirror-dynamic-theme&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd codemirror-dynamic-theme
npm i lit codemirror @material/material-color-utilities
npm i -D @types/node @types/codemirror
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &amp;quot;/codemirror-dynamic-theme/&amp;quot;,
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;CodeMirror Dynamic Theme&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/code-window.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
        padding: 0;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;code-window&amp;gt; &amp;lt;/code-window&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Web Component&lt;/h2&gt;
&lt;p&gt;Before we update our component we need to rename &lt;code&gt;my-element.ts&lt;/code&gt; to &lt;code&gt;code-window.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Open up &lt;code&gt;code-window.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement, unsafeCSS } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;
import {
  applyTheme,
  argbFromHex,
  hexFromArgb,
  themeFromSourceColor,
} from &amp;quot;@material/material-color-utilities&amp;quot;;
import CodeMirror from &amp;quot;codemirror&amp;quot;;
import codemirrorStyles from &amp;quot;codemirror/lib/codemirror.css&amp;quot;;

@customElement(&amp;quot;code-window&amp;quot;)
export class CodeWindow extends LitElement {
  static styles = css`
    ${unsafeCSS(codemirrorStyles)}

    main {
      width: 100vw;
      height: 100vh;
      background-color: var(--md-sys-color-background);
      color: var(--md-sys-color-on-background);
      --header-height: 48px;
      --input-size: 32px;
    }

    .toolbar {
      height: var(--header-height);
      background-color: var(--md-sys-color-primary-container);
      color: var(--md-sys-color-on-primary-container);
      display: flex;
      align-items: center;
    }

    .actions &amp;gt; * {
      margin-left: 4px;
      margin-right: 4px;
    }

    .toolbar .title {
      font-family: sans-serif;
      font-size: 18px;
      padding-left: 4px;
    }

    .toolbar .actions {
      display: flex;
      align-items: center;
    }

    .toolbar a {
      padding: 0;
      margin: 0;
      padding-left: 8px;
      padding-right: 8px;
      display: flex;
      align-items: center;
      cursor: pointer;
    }

    input[type=&amp;quot;color&amp;quot;] {
      width: calc(var(--input-size) * 2);
      height: var(--input-size);
      outline: none;
      border: none;
      border-radius: 50%;
      background-color: var(--md-sys-color-primary-container);
    }
    input[type=&amp;quot;color&amp;quot;]::-webkit-color-swatch-wrapper {
      padding: 0;
    }
    input[type=&amp;quot;color&amp;quot;]::-webkit-color-swatch {
      border: none;
      border-radius: var(--input-size);
      border: var(--md-sys-color-outline) solid 1px;
    }

    button {
      border: none;
      border-radius: 4px;
      padding: 8px;
    }

    .tertiary {
      background-color: var(--md-sys-color-tertiary);
      color: var(--md-sys-color-on-tertiary);
    }

    .secondary {
      background-color: var(--md-sys-color-secondary);
      color: var(--md-sys-color-on-secondary);
    }

    .spacer {
      flex: 1;
    }

    .editor {
      height: calc(100% - var(--header-height));
      width: 100%;
    }
  `;

  @property() value = [
    `import {html, css, LitElement} from &apos;lit&apos;;`,
    `import {customElement, property} from &apos;lit/decorators.js&apos;;`,
    ``,
    `@customElement(&apos;simple-greeting&apos;)`,
    `export class SimpleGreeting extends LitElement {`,
    `  static styles = css\`p { color: blue }\`;`,
    ``,
    `  @property()`,
    `  name = &apos;Somebody&apos;;`,
    ``,
    `  render() {`,
    `    return html\`&amp;lt;p&amp;gt;Hello, \${this.name}!&amp;lt;/p&amp;gt;\`;`,
    `  }`,
    `}`,
  ].join(&amp;quot;\n&amp;quot;);

  @property() color = &amp;quot;#6750A4&amp;quot;;
  @property({ type: Boolean }) dark = window.matchMedia(
    &amp;quot;(prefers-color-scheme: dark)&amp;quot;
  ).matches;

  render() {
    return html`&amp;lt;main&amp;gt;
      &amp;lt;header class=&amp;quot;toolbar&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;${document.title}&amp;lt;/div&amp;gt;
        &amp;lt;div class=&amp;quot;spacer&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div class=&amp;quot;actions&amp;quot;&amp;gt;
          &amp;lt;button class=&amp;quot;secondary&amp;quot; @click=${this.toggleDark.bind(this)}&amp;gt;
            ${this.dark ? &amp;quot;Light&amp;quot; : &amp;quot;Dark&amp;quot;}
          &amp;lt;/button&amp;gt;
          &amp;lt;button class=&amp;quot;tertiary&amp;quot; @click=${this.randomColor.bind(this)}&amp;gt;
            Random
          &amp;lt;/button&amp;gt;
          &amp;lt;input
            type=&amp;quot;color&amp;quot;
            .value=${this.color}
            @input=${this.onColor.bind(this)}
          /&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/header&amp;gt;
      &amp;lt;div class=&amp;quot;editor&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/main&amp;gt;`;
  }

  firstUpdated() {
    const root = this.shadowRoot!.querySelector(&amp;quot;.editor&amp;quot;) as HTMLElement;
    const editor = CodeMirror(root, {
      value: this.value,
      mode: &amp;quot;javascript&amp;quot;,
      lineNumbers: true,
      lineWrapping: true,
      indentUnit: 4,
      tabSize: 4,
      indentWithTabs: true,
      autofocus: true,
    });
    console.debug(editor);
    editor.setSize(&amp;quot;100%&amp;quot;, `100%`);
    this.updateTheme();
    window
      .matchMedia(&amp;quot;(prefers-color-scheme: dark)&amp;quot;)
      .addEventListener(&amp;quot;change&amp;quot;, (e) =&amp;gt; {
        this.dark = e.matches;
        this.updateTheme();
      });
  }

  private updateTheme() {
   // TODO: Generate Theme
  }

  private setColor(val: string) {
    this.color = val;
    this.updateTheme();
  }

  private onColor(e: Event) {
    const target = e.target as HTMLInputElement;
    this.setColor(target.value);
  }

  private randomColor() {
    const letters = &amp;quot;0123456789ABCDEF&amp;quot;;
    let color = &amp;quot;#&amp;quot;;
    for (let i = 0; i &amp;lt; 6; i++) {
      color += letters[Math.floor(Math.random() * 16)];
    }
    this.setColor(color);
  }

  private toggleDark() {
    this.dark = !this.dark;
    this.updateTheme();
  }

}

declare global {
  interface HTMLElementTagNameMap {
    &amp;quot;code-window&amp;quot;: CodeWindow;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are setting up some of the editor basics to load in the styles needed for the basic layout.&lt;/p&gt;
&lt;p&gt;There are also few methods that handle updating of properties on the element such as &lt;code&gt;toggleDark&lt;/code&gt; and &lt;code&gt;setColor&lt;/code&gt;. When you run the application you should see the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/cm_1_vrf88lj26h.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It doesn&apos;t look great yet, but now we can add a CodeMirror theme to import. Create a file &lt;code&gt;src/theme.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;import { css } from &amp;quot;lit&amp;quot;;

export const codeMirrorTheme = css`
  .CodeMirror {
    background-color: var(--md-sys-color-background);
    color: var(--md-sys-color-on-background);
  }

  .CodeMirror-gutters {
    background: var(--md-sys-color-surface-variant);
    color: var(--md-sys-color-on-surface-variant);
    border: none;
  }

  .CodeMirror-guttermarker,
  .CodeMirror-guttermarker-subtle,
  .CodeMirror-linenumber {
    color: var(--md-sys-color-on-background);
  }

  .CodeMirror-cursor {
    border-left: 1px solid var(--md-sys-color-primary);
  }
  .cm-fat-cursor .CodeMirror-cursor {
    background-color: var(--md-sys-color-background);
  }
  .cm-animate-fat-cursor {
    background-color: var(--md-sys-color-background);
  }

  div.CodeMirror-selected {
    background: var(--md-sys-color-surface-variant);
  }

  .CodeMirror-focused div.CodeMirror-selected {
    background: var(--md-sys-color-surface-variant);
  }

  .CodeMirror-line::selection,
  .CodeMirror-line &amp;gt; span::selection,
  .CodeMirror-line &amp;gt; span &amp;gt; span::selection {
    background: var(--md-sys-color-surface-variant);
  }

  .CodeMirror-line::-moz-selection,
  .CodeMirror-line &amp;gt; span::-moz-selection,
  .CodeMirror-line &amp;gt; span &amp;gt; span::-moz-selection {
    background: var(--md-sys-color-surface-variant);
  }

  .CodeMirror-activeline-background {
    background: var(--md-sys-color-surface);
  }

  .cm-keyword {
    color: var(--md-custom-color-keyword) !important;
  }

  .cm-operator {
    color: var(--md-custom-color-operator) !important;
  }

  .cm-variable-2 {
    color: var(--md-custom-color-variable-2) !important;
  }

  .cm-variable-3,
  .cm-type {
    color: var(--md-custom-color-variable-3) !important;
  }

  .cm-builtin {
    color: var(--md-custom-color-builtin) !important;
  }

  .cm-atom {
    color: var(--md-custom-color-atom) !important;
  }

  .cm-number {
    color: var(--md-custom-color-number) !important;
  }

  .cm-def {
    color: var(--md-custom-color-def) !important;
  }

  .cm-string {
    color: var(--md-custom-color-string) !important;
  }

  .cm-string-2 {
    color: var(--md-custom-color-string-2) !important;
  }

  .cm-comment {
    color: var(--md-custom-color-comment) !important;
  }

  .cm-variable {
    color: var(--md-custom-color-variable) !important;
  }

  .cm-tag {
    color: var(--md-custom-color-tag) !important;
  }

  .cm-meta {
    color: var(--md-custom-color-meta) !important;
  }

  .cm-attribute {
    color: var(--md-custom-color-attribute) !important;
  }

  .cm-property {
    color: var(--md-custom-color-property) !important;
  }

  .cm-qualifier {
    color: var(--md-custom-color-qualifier) !important;
  }

  .cm-variable-3,
  .cm-type {
    color: var(--md-custom-color-variable-3) !important;
  }

  .cm-error {
    color: var(--md-sys-color-on-error);
    background-color: var(--md-sys-color-error);
  }

  .CodeMirror-matchingbracket {
    text-decoration: underline;
    color: var(--md-sys-color-on-surface);
  }
`;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are defining all the styles as &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;CSS Custom Properties&lt;/a&gt; so we can easily update them.&lt;/p&gt;
&lt;p&gt;Now import the theme and apply the styles in the component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { codeMirrorTheme } from &amp;quot;./theme&amp;quot;;

@customElement(&amp;quot;code-window&amp;quot;)
export class CodeWindow extends LitElement {
  static styles = css`
    ${unsafeCSS(codemirrorStyles)}
    ${codeMirrorTheme}
    ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to implement the &lt;code&gt;updateTheme&lt;/code&gt; method in our element:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;updateTheme() {
    const source = this.color;
    const dark = this.dark;
    const target = this.shadowRoot!.querySelector(&amp;quot;main&amp;quot;) as HTMLElement;
    const properties = [
        `--md-custom-color-keyword: #c75779;`,
        `--md-custom-color-operator: #008800;`,
        `--md-custom-color-variable: #90ccff;`,
        `--md-custom-color-variable-2: #dd7700;`,
        `--md-custom-color-variable-3: #3333bb;`,
        `--md-custom-color-variable-3: #decb6b;`,
        `--md-custom-color-builtin: #003388;`,
        `--md-custom-color-atom: #bb4646;`,
        `--md-custom-color-number: #b4c5ff;`,
        `--md-custom-color-def: #82aaff;`,
        `--md-custom-color-string: #ffb4a9;`,
        `--md-custom-color-string-2: #ffb4a9;`,
        `--md-custom-color-comment: #888888;`,
        `--md-custom-color-tag: #000080;`,
        `--md-custom-color-meta: #a9c7ff;`,
        `--md-custom-color-attribute: #008080;`,
        `--md-custom-color-property: #336699;`,
        `--md-custom-color-qualifier: #690;`,
    ];
    const customColors = properties.map((property) =&amp;gt; {
        const [key, value] = property.split(&amp;quot;:&amp;quot;);
        const name = key.trim().replace(/^--md-custom-color-/, &amp;quot;&amp;quot;);
        const color = argbFromHex(value.trim().replace(&amp;quot;;&amp;quot;, &amp;quot;&amp;quot;));
        return {
        name,
        value: color,
        blend: true,
        };
    });
    const theme = themeFromSourceColor(argbFromHex(source), customColors);
    applyTheme(theme, { target, dark });
    for (const custom of theme.customColors) {
        const name = custom.color.name;
        const section = dark ? custom.dark : custom.light;
        target.style.setProperty(
        `--md-custom-color-${name}`,
        hexFromArgb(section.color)
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are using the &lt;a href=&quot;https://github.com/material-foundation/material-color-utilities&quot;&gt;Material Color Utilities&lt;/a&gt; package and generating a theme from a source color. We can take advantage of Custom Colors to blend the values to the theme.&lt;/p&gt;
&lt;p&gt;After the theme is generated we can apply them to the root element and have the custom properties update the editor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/cm_2_i38ka2mknd.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Changing the source color can update the theme:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/cm_3_h3mcbh0c3s.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Changing the brightness can set the colors as well:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/cm_4_u36e81hjiw.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/cm_5_2xo33gqwdh.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building with Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source for this example can be found &lt;a href=&quot;https://github.com/rodydavis/codemirror-dynamic-theme&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to check out dynamic themes for VSCode I created an extension &lt;a href=&quot;https://github.com/rodydavis/vscode-dynamic-theme&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Lit and Figma</title><link>https://rodydavis.com/lit/figma-interop/</link><guid isPermaLink="true">https://rodydavis.com/lit/figma-interop/</guid><description>Learn how to integrate Lit web components into Figma by setting up a comprehensive plugin development environment using Node.js and TypeScript.</description><pubDate>Mon, 20 Jan 2025 02:00:23 GMT</pubDate><content:encoded>&lt;h1&gt;Lit and Figma&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to create a figma plugin.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; You can find the final source &lt;a href=&quot;https://github.com/rodydavis/figma_lit_example&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Figma Desktop&lt;/li&gt;
&lt;li&gt;Node&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by creating a empty directory and naming it with &lt;code&gt;snake_case&lt;/code&gt; whatever we want.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir figma_lit_example
cd figma_lit_example
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Web Setup&lt;/h3&gt;
&lt;p&gt;Now we are in the &lt;code&gt;figma_lit_example&lt;/code&gt; directory and can setup Figma and Lit. Let&apos;s start with node.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will setup the basics for a node project and install the packages we need. Now lets add some config files. Now open the &lt;code&gt;package.json&lt;/code&gt; and replace it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &amp;quot;name&amp;quot;: &amp;quot;figma_lit_example&amp;quot;,
  &amp;quot;version&amp;quot;: &amp;quot;1.0.0&amp;quot;,
  &amp;quot;description&amp;quot;: &amp;quot;Lit Figma Plugin&amp;quot;,
  &amp;quot;dependencies&amp;quot;: {
    &amp;quot;lit&amp;quot;: &amp;quot;^2.0.0-rc.1&amp;quot;
  },
  &amp;quot;devDependencies&amp;quot;: {
    &amp;quot;@figma/plugin-typings&amp;quot;: &amp;quot;^1.23.0&amp;quot;,
    &amp;quot;html-webpack-inline-source-plugin&amp;quot;: &amp;quot;^1.0.0-beta.2&amp;quot;,
    &amp;quot;html-webpack-plugin&amp;quot;: &amp;quot;^4.3.0&amp;quot;,
    &amp;quot;css-loader&amp;quot;: &amp;quot;^5.2.4&amp;quot;,
    &amp;quot;ts-loader&amp;quot;: &amp;quot;^8.0.0&amp;quot;,
    &amp;quot;typescript&amp;quot;: &amp;quot;^4.2.4&amp;quot;,
    &amp;quot;url-loader&amp;quot;: &amp;quot;^4.1.1&amp;quot;,
    &amp;quot;webpack&amp;quot;: &amp;quot;^4.44.1&amp;quot;,
    &amp;quot;webpack-cli&amp;quot;: &amp;quot;^4.6.0&amp;quot;
  },
  &amp;quot;scripts&amp;quot;: {
    &amp;quot;dev&amp;quot;: &amp;quot;npx webpack --mode=development --watch&amp;quot;,
    &amp;quot;copy&amp;quot;: &amp;quot;mkdir -p lit-plugin &amp;amp;&amp;amp; cp ./manifest.json ./lit-plugin/manifest.json &amp;amp;&amp;amp; cp ./dist/ui.html ./lit-plugin/ui.html &amp;amp;&amp;amp; cp ./dist/code.js ./lit-plugin/code.js&amp;quot;,
    &amp;quot;build&amp;quot;: &amp;quot;npx webpack --mode=production &amp;amp;&amp;amp; npm run copy&amp;quot;,
    &amp;quot;zip&amp;quot;: &amp;quot;npm run build &amp;amp;&amp;amp; zip -r lit-plugin.zip lit-plugin&amp;quot;
  },
  &amp;quot;browserslist&amp;quot;: [
    &amp;quot;last 1 Chrome versions&amp;quot;
  ],
  &amp;quot;keywords&amp;quot;: [],
  &amp;quot;author&amp;quot;: &amp;quot;&amp;quot;,
  &amp;quot;license&amp;quot;: &amp;quot;ISC&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will add everything we need and add the scripts we need for development and production. Then run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm i
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install everything we need to get started. Now we need to setup some config files.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;touch tsconfig.json
touch webpack.config.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create 2 files. Now open up &lt;code&gt;tsconfig.json&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &amp;quot;compilerOptions&amp;quot;: {
    &amp;quot;target&amp;quot;: &amp;quot;es2017&amp;quot;,
    &amp;quot;module&amp;quot;: &amp;quot;esNext&amp;quot;,
    &amp;quot;moduleResolution&amp;quot;: &amp;quot;node&amp;quot;,
    &amp;quot;lib&amp;quot;: [&amp;quot;es2017&amp;quot;, &amp;quot;dom&amp;quot;, &amp;quot;dom.iterable&amp;quot;],
    &amp;quot;typeRoots&amp;quot;: [&amp;quot;./node_modules/@types&amp;quot;, &amp;quot;./node_modules/@figma&amp;quot;],
    &amp;quot;declaration&amp;quot;: true,
    &amp;quot;sourceMap&amp;quot;: true,
    &amp;quot;inlineSources&amp;quot;: true,
    &amp;quot;noUnusedLocals&amp;quot;: true,
    &amp;quot;noImplicitReturns&amp;quot;: true,
    &amp;quot;noFallthroughCasesInSwitch&amp;quot;: true,
    &amp;quot;experimentalDecorators&amp;quot;: true,
    &amp;quot;skipLibCheck&amp;quot;: true,
    &amp;quot;strict&amp;quot;: true,
    &amp;quot;noImplicitAny&amp;quot;: false,
    &amp;quot;outDir&amp;quot;: &amp;quot;./lib&amp;quot;,
    &amp;quot;baseUrl&amp;quot;: &amp;quot;./packages&amp;quot;,
    &amp;quot;importHelpers&amp;quot;: true,
    &amp;quot;plugins&amp;quot;: [
      {
        &amp;quot;name&amp;quot;: &amp;quot;ts-lit-plugin&amp;quot;,
        &amp;quot;rules&amp;quot;: {
          &amp;quot;no-unknown-tag-name&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-unclosed-tag&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-unknown-property&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-unintended-mixed-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-invalid-boolean-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-expressionless-property-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-noncallable-event-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-boolean-in-attribute-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-complex-attribute-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-nullable-attribute-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-incompatible-type-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-invalid-directive-binding&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-incompatible-property-type&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-unknown-property-converter&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-invalid-attribute-name&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-invalid-tag-name&amp;quot;: &amp;quot;error&amp;quot;,
          &amp;quot;no-unknown-attribute&amp;quot;: &amp;quot;off&amp;quot;,
          &amp;quot;no-unknown-event&amp;quot;: &amp;quot;off&amp;quot;,
          &amp;quot;no-unknown-slot&amp;quot;: &amp;quot;off&amp;quot;,
          &amp;quot;no-invalid-css&amp;quot;: &amp;quot;off&amp;quot;
        }
      }
    ]
  },
  &amp;quot;include&amp;quot;: [&amp;quot;src/**/*.ts&amp;quot;],
  &amp;quot;references&amp;quot;: []
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a basic typescript config. Now open up &lt;code&gt;webpack.config.ts&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const HtmlWebpackInlineSourcePlugin = require(&amp;quot;html-webpack-inline-source-plugin&amp;quot;);
const HtmlWebpackPlugin = require(&amp;quot;html-webpack-plugin&amp;quot;);
const path = require(&amp;quot;path&amp;quot;);

module.exports = (env, argv) =&amp;gt; ({
  mode: argv.mode === &amp;quot;production&amp;quot; ? &amp;quot;production&amp;quot; : &amp;quot;development&amp;quot;,
  devtool: argv.mode === &amp;quot;production&amp;quot; ? false : &amp;quot;inline-source-map&amp;quot;,
  entry: {
    ui: &amp;quot;./src/ui.ts&amp;quot;,
    code: &amp;quot;./src/code.ts&amp;quot;,
    app: &amp;quot;./src/my-app.ts&amp;quot;,
  },
  module: {
    rules: [
      { test: /\.tsx?$/, use: &amp;quot;ts-loader&amp;quot;, exclude: /node_modules/ },
      { test: /\.css$/, use: [&amp;quot;style-loader&amp;quot;, { loader: &amp;quot;css-loader&amp;quot; }] },
      { test: /\.(png|jpg|gif|webp|svg)$/, loader: &amp;quot;url-loader&amp;quot; },
    ],
  },
  resolve: { extensions: [&amp;quot;.ts&amp;quot;, &amp;quot;.js&amp;quot;] },
  output: {
    filename: &amp;quot;[name].js&amp;quot;,
    path: path.resolve(__dirname, &amp;quot;dist&amp;quot;),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, &amp;quot;ui.html&amp;quot;),
      filename: &amp;quot;ui.html&amp;quot;,
      inject: true,
      inlineSource: &amp;quot;.(js|css)$&amp;quot;,
      chunks: [&amp;quot;ui&amp;quot;],
    }),
    new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
  ],
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to create the ui for the plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;touch ui.html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open up &lt;code&gt;/src/ui.html&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;my-app&amp;gt;&amp;lt;/my-app&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need a manifest file for the figma plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;touch manifest.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;manifest.json&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &amp;quot;name&amp;quot;: &amp;quot;figma_lit_example&amp;quot;,
  &amp;quot;id&amp;quot;: &amp;quot;973668777853442323&amp;quot;,
  &amp;quot;api&amp;quot;: &amp;quot;1.0.0&amp;quot;,
  &amp;quot;main&amp;quot;: &amp;quot;code.js&amp;quot;,
  &amp;quot;ui&amp;quot;: &amp;quot;ui.html&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to create our web component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir src
cd src
touch my-app.ts
touch code.ts
touch ui.ts
cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;/src/ui.ts&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import &amp;quot;./my-app&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;/src/my-app.ts&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, query } from &amp;quot;lit/decorators.js&amp;quot;;

@customElement(&amp;quot;my-app&amp;quot;)
export class MyApp extends LitElement {
  @property() amount = &amp;quot;5&amp;quot;;
  @query(&amp;quot;#count&amp;quot;) countInput!: HTMLInputElement;

  render() {
    return html`
      &amp;lt;div&amp;gt;
        &amp;lt;h2&amp;gt;Rectangle Creator&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;Count: &amp;lt;input id=&amp;quot;count&amp;quot; value=&amp;quot;${this.amount}&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;button id=&amp;quot;create&amp;quot; @click=${this.create}&amp;gt;Create&amp;lt;/button&amp;gt;
        &amp;lt;button id=&amp;quot;cancel&amp;quot; @click=${this.cancel}&amp;gt;Cancel&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    `;
  }

  create() {
    const count = parseInt(this.countInput.value, 10);
    this.sendMessage(&amp;quot;create-rectangles&amp;quot;, { count });
  }

  cancel() {
    this.sendMessage(&amp;quot;cancel&amp;quot;);
  }

  private sendMessage(type: string, content: Object = {}) {
    const message = { pluginMessage: { type: type, ...content } };
    parent.postMessage(message, &amp;quot;*&amp;quot;);
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;code.ts&lt;/code&gt; and paste the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const options: ShowUIOptions = {
  width: 250,
  height: 200,
};

figma.showUI(__html__, options);

figma.ui.onmessage = msg =&amp;gt; {
  switch (msg.type) {
    case &apos;create-rectangles&apos;:
      const nodes: SceneNode[] = [];
      for (let i = 0; i &amp;lt; msg.count; i++) {
        const rect = figma.createRectangle();
        rect.x = i * 150;
        rect.fills = [{ type: &apos;SOLID&apos;, color: { r: 1, g: 0.5, b: 0 } }];
        figma.currentPage.appendChild(rect);
        nodes.push(rect);
      }
      figma.currentPage.selection = nodes;
      figma.viewport.scrollAndZoomIntoView(nodes);
      break;
    default:
      break;
  }

  figma.closePlugin();
};

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Building the Plugin&lt;/h2&gt;
&lt;p&gt;Now that we have all the code in place we can build the plugin and test it in Figma.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 1&lt;/h4&gt;
&lt;p&gt;Download and open the desktop version of Figma.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.figma.com/downloads/&quot;&gt;https://www.figma.com/downloads/&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Step 2&lt;/h4&gt;
&lt;p&gt;Open the menu and navigate to “Plugins &amp;gt; Manage plugins”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/f_1_kfp7k3aclm.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 3&lt;/h4&gt;
&lt;p&gt;Click on the plus icon to add a local plugin.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/f_2_m4hgctvnry.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Click on the box to link to an existing plugin to navigate to the &lt;code&gt;lit-plugin&lt;/code&gt; folder that was created after the build process in your source code and select &lt;code&gt;manifest.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/f_3_ufrd6v1644.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 4&lt;/h4&gt;
&lt;p&gt;To run the plugin navigate to “Plugins &amp;gt; Development &amp;gt; figma_lit_example” to launch your plugin.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/f_4_uoz37dtck3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 5&lt;/h4&gt;
&lt;p&gt;Now your plugin should launch and you can create 5 rectangles on the canvas.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/f_5_496mr4f5wp.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If everything worked you will have 5 new rectangles on the canvas focused by figma.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/f_6_kp2j5tc6xw.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;WASM Support&lt;/h2&gt;
&lt;p&gt;If there is a heavy computation that could benefit from running in &lt;a href=&quot;https://webassembly.org/&quot;&gt;WebAssembly&lt;/a&gt; the following will ensure that it is hardware accelerated when possible.&lt;/p&gt;
&lt;p&gt;Let&apos;s add &lt;a href=&quot;https://www.assemblyscript.org/&quot;&gt;AssemblyScript&lt;/a&gt; and some dependencies that will be used for loading the WASM into the figma ui.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm i @assemblyscript/loader
npm i --D assemblyscript js-inline-wasm
npx asinit .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Confirm yes to the prompt to have it generate the project files and add the following to the scripts in &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;&amp;quot;asbuild:untouched&amp;quot;: &amp;quot;asc assembly/index.ts --target debug&amp;quot;,
&amp;quot;asbuild:optimized&amp;quot;: &amp;quot;asc assembly/index.ts --target release&amp;quot;,
&amp;quot;asbuild&amp;quot;: &amp;quot;npm run asbuild:untouched &amp;amp;&amp;amp; npm run asbuild:optimized&amp;quot;,
&amp;quot;inlinewasm&amp;quot;: &amp;quot;inlinewasm build/optimized.wasm --output src/wasm.ts&amp;quot;,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code that will be used for the WASM is in &lt;code&gt;/assembly/index.ts&lt;/code&gt; and it should show the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// The entry file of your WebAssembly module.

export function add(a: i32, b: i32): i32 {
  return a + b;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&apos;s build the wasm module:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run asbuild
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the wasm build to be ignored for git add the following to .gitignore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate the wasm and wat files in the build directory, but for figma to load them into the ui it needs to be inlined so run the following command to generate the js from the wasm file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run inlinewasm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should generate &lt;code&gt;src/wasm.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const encoded = &apos;AGFzbQEAAAABBwFgAn9/AX8DAgEABQMBAAAHEAIDYWRkAAAGbWVtb3J5AgAKCQEHACAAIAFqCwAmEHNvdXJjZU1hcHBpbmdVUkwULi9vcHRpbWl6ZWQud2FzbS5tYXA=&apos;;
export default new Promise(resolve =&amp;gt; {
    const decoded = atob(encoded);
    const len = decoded.length;
    const bytes = new Uint8Array(len);
    for (var i = 0; i &amp;lt; len; i++) {
        bytes[i] = decoded.charCodeAt(i);
    }
    resolve(new Response(bytes, { status: 200, headers: { &amp;quot;Content-Type&amp;quot;: &amp;quot;application/wasm&amp;quot; } }));
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now open up the &lt;code&gt;/src/my-app.ts&lt;/code&gt; and update with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property, query } from &amp;quot;lit/decorators.js&amp;quot;;

@customElement(&amp;quot;my-app&amp;quot;)
export class MyApp extends LitElement {
  @property() amount = &amp;quot;5&amp;quot;; // &amp;lt;-- Pass in a value for the number of rectangles to create
  @query(&amp;quot;#count&amp;quot;) countInput!: HTMLInputElement;

  render() {
    return html`
      &amp;lt;div&amp;gt;
        &amp;lt;h2&amp;gt;Rectangle Creator&amp;lt;/h2&amp;gt;
        &amp;lt;!-- Pass in the amount to the input value --&amp;gt;
        &amp;lt;p&amp;gt;Count: &amp;lt;input id=&amp;quot;count&amp;quot; value=&amp;quot;${this.amount}&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;
        ...
      &amp;lt;/div&amp;gt;
    `;
  }
  ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will let us pass in the amount of boxes to create externally.&lt;/p&gt;
&lt;p&gt;Now open &lt;code&gt;/src/ui.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import &amp;quot;./my-app&amp;quot;;

import wasm from &amp;quot;./wasm&amp;quot;; // &amp;lt;-- Our WASM file to load

WebAssembly.instantiateStreaming(wasm as Promise&amp;lt;Response&amp;gt;).then((obj) =&amp;gt; {
  // @ts-ignore
  const value: number = obj.instance.exports.add(2, 4);
  console.log(&amp;quot;return from wasm&amp;quot;, value);
  const elem = document.querySelector(&apos;my-app&apos;)! as HTMLElement;
  elem.setAttribute(&apos;amount&apos;, `${value}`);
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when we build the plugin and run it in figma the amount of boxes will be the result of calling into wasm!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building a plugin in Figma you can read more &lt;a href=&quot;https://www.figma.com/plugin-docs/intro/&quot;&gt;here&lt;/a&gt; and for Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Draggable DOM with Lit</title><link>https://rodydavis.com/lit/draggable-dom/</link><guid isPermaLink="true">https://rodydavis.com/lit/draggable-dom/</guid><description>Learn how to create an interactive, draggable DOM using Lit web components, CSS transforms, and slots.</description><pubDate>Sun, 19 Jan 2025 08:19:15 GMT</pubDate><content:encoded>&lt;h1&gt;Draggable DOM with Lit&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to create a interactive dom with CSS transforms and slots.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/lit-draggable-dom&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/lit-draggable-dom/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;lit-draggable-dom&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd lit-draggable-dom
npm i lit
npm i -D @types/node
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &amp;quot;/lit-draggable-dom/&amp;quot;,
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
    },
  },
});

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Lit Draggable DOM&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100vh;
      }
    &amp;lt;/style&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/draggable-dom.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;draggable-dom&amp;gt;
      &amp;lt;img
        src=&amp;quot;https://lit.dev/images/logo.svg&amp;quot;
        alt=&amp;quot;Lit Logo&amp;quot;
        width=&amp;quot;500&amp;quot;
        height=&amp;quot;333&amp;quot;
        style=&amp;quot;--dx: 59.4909px; --dy: 32.8429px&amp;quot;
      /&amp;gt;
      &amp;lt;svg width=&amp;quot;400&amp;quot; height=&amp;quot;110&amp;quot; style=&amp;quot;--dx: 230.057px; --dy: 33.6257px&amp;quot;&amp;gt;
        &amp;lt;rect
          width=&amp;quot;400&amp;quot;
          height=&amp;quot;100&amp;quot;
          style=&amp;quot;fill: rgb(0, 0, 255); stroke-width: 3; stroke: rgb(0, 0, 0)&amp;quot;
        /&amp;gt;
      &amp;lt;/svg&amp;gt;
      &amp;lt;svg height=&amp;quot;100&amp;quot; width=&amp;quot;100&amp;quot;&amp;gt;
        &amp;lt;circle
          cx=&amp;quot;50&amp;quot;
          cy=&amp;quot;50&amp;quot;
          r=&amp;quot;40&amp;quot;
          stroke=&amp;quot;black&amp;quot;
          stroke-width=&amp;quot;3&amp;quot;
          fill=&amp;quot;red&amp;quot;
        /&amp;gt;
      &amp;lt;/svg&amp;gt;
    &amp;lt;/draggable-dom&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are setting up the &lt;code&gt;lit-element&lt;/code&gt; to have a few slots which can be any valid HTML or SVG Elements.&lt;/p&gt;
&lt;p&gt;It is optional to set the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/--*&quot;&gt;css custom properties&lt;/a&gt; &lt;code&gt;--dx&lt;/code&gt; and &lt;code&gt;--dy&lt;/code&gt; as this is just the initial positions on the canvas.&lt;/p&gt;
&lt;h2&gt;Web Component&lt;/h2&gt;
&lt;p&gt;Before we update our component we need to rename &lt;code&gt;my-element.ts&lt;/code&gt; to &lt;code&gt;draggable-dom.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Open up &lt;code&gt;draggable-dom.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, query } from &amp;quot;lit/decorators.js&amp;quot;;

type DragType = &amp;quot;none&amp;quot; | &amp;quot;canvas&amp;quot; | &amp;quot;element&amp;quot;;
type SupportedNode = HTMLElement | SVGElement;

@customElement(&amp;quot;draggable-dom&amp;quot;)
export class CSSCanvas extends LitElement {
  @query(&amp;quot;main&amp;quot;) root!: HTMLElement;
  @query(&amp;quot;#children&amp;quot;) container!: HTMLElement;
  @query(&amp;quot;canvas&amp;quot;) canvas!: HTMLCanvasElement;
  dragType: DragType = &amp;quot;none&amp;quot;;
  offset: Offset = { x: 0, y: 0 };
  pointerMap: Map&amp;lt;number, PointerData&amp;gt; = new Map();

  static styles = css`
    :host {
      --offset-x: 0;
      --offset-y: 0;
      --grid-background-color: white;
      --grid-color: black;
      --grid-size: 40px;
      --grid-dot-size: 1px;
    }
    main {
      overflow: hidden;
    }
    canvas {
      background-size: var(--grid-size) var(--grid-size);
      background-image: radial-gradient(
        circle,
        var(--grid-color) var(--grid-dot-size),
        var(--grid-background-color) var(--grid-dot-size)
      );
      background-position: var(--offset-x) var(--offset-y);
      z-index: 0;
    }
    .full-size {
      width: 100%;
      height: 100%;
      position: fixed;
    }
    .child {
      --dx: 0px;
      --dy: 0px;
      position: fixed;
      flex-shrink: 1;
      z-index: var(--layer, 0);
      transform: translate(var(--dx), var(--dy));
    }
    @media (prefers-color-scheme: dark) {
      main {
        --grid-background-color: black;
        --grid-color: grey;
      }
    }
  `;

  render() {
    return html`
      &amp;lt;main class=&amp;quot;full-size&amp;quot;&amp;gt;
        &amp;lt;canvas class=&amp;quot;full-size&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;
        &amp;lt;div id=&amp;quot;children&amp;quot; class=&amp;quot;full-size&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
    `;
  }
}

interface Offset {
  x: number;
  y: number;
}

interface PointerData {
  id: number;
  startPos: Offset;
  currentPos: Offset;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are just setting up some boilerplate to render a &lt;code&gt;main&lt;/code&gt; element with a &lt;code&gt;canvas&lt;/code&gt; element as a background and the &lt;code&gt;div&lt;/code&gt; element to contain the canvas elements.&lt;/p&gt;
&lt;p&gt;We are also making sure to clip and only render what is visible.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Offset&lt;/code&gt; and &lt;code&gt;PointerData&lt;/code&gt; interfaces will be used for storing the location of each pointer interacting with the screen.&lt;/p&gt;
&lt;p&gt;When the user has dark mode enabled for the system it will change the colors of the canvas grid.&lt;/p&gt;
&lt;p&gt;Now let&apos;s add the slot children to the canvas by adding the following to the class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;async firstUpdated() {
    const items = Array.from(this.childNodes);
    let i = 0;
    for (const node of items) {
        if (node instanceof SVGElement || node instanceof HTMLElement) {
            const child = node as SupportedNode;
            child.classList.add(&amp;quot;child&amp;quot;);
            child.style.setProperty(&amp;quot;--layer&amp;quot;, `${i}`);
            this.container.append(child);
            child.addEventListener(&amp;quot;pointerdown&amp;quot;, (e: any) =&amp;gt; {
                // Pointer Down for Child
            });
            child.addEventListener(&amp;quot;pointermove&amp;quot;, (e: any) =&amp;gt; {
                // Pointer Move for Child
            });
            i++;
        }
    }
    this.requestUpdate();
    this.root.addEventListener(&amp;quot;pointerdown&amp;quot;, (e: any) =&amp;gt; {
        // Pointer Down for Canvas
    });
    this.root.addEventListener(&amp;quot;pointermove&amp;quot;, (e: any) =&amp;gt; {
        // Pointer Move for Canvas
    });
    this.root.addEventListener(&amp;quot;pointerup&amp;quot;, (e: any) =&amp;gt; {
        // Pointer Up for Canvas
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The order of the slots defines what renders on top of each other. For each item in the slot it sets&lt;code&gt;--layer&lt;/code&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/z-index&quot;&gt;&lt;code&gt;z-index&lt;/code&gt;&lt;/a&gt; to the current index.&lt;/p&gt;
&lt;p&gt;Currently nothing is happening when we interact with the elements but things should be rendering.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/dom_1_8lmqmje55f.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now let&apos;s add the event handlers for the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events&quot;&gt;pointer events&lt;/a&gt; by appending the following to the class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;handleDown(event: PointerEvent, type: DragType) {
    if (this.dragType === &amp;quot;none&amp;quot;) {
        event.preventDefault();
        this.dragType = type;
        (event.target as Element).setPointerCapture(event.pointerId);
        this.pointerMap.set(event.pointerId, {
            id: event.pointerId,
            startPos: { x: event.clientX, y: event.clientY },
            currentPos: { x: event.clientX, y: event.clientY },
        });
    }
}

handleMove(
    event: PointerEvent,
    type: DragType,
    onMove: (delta: Offset) =&amp;gt; void
) {
    if (this.dragType === type) {
        event.preventDefault();
        const saved = this.pointerMap.get(event.pointerId)!;
        const current = { ...saved.currentPos };
        saved.currentPos = { x: event.clientX, y: event.clientY };
        const delta = {
            x: saved.currentPos.x - current.x,
            y: saved.currentPos.y - current.y,
        };
        onMove(delta);
    }
}

handleUp(event: PointerEvent) {
    this.dragType = &amp;quot;none&amp;quot;;
    (event.target as Element).releasePointerCapture(event.pointerId);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For each event we want to check if the current event &lt;code&gt;canvas&lt;/code&gt; or &lt;code&gt;element&lt;/code&gt; so if we start moving an element it doesn&apos;t move the canvas and vice versa.&lt;/p&gt;
&lt;p&gt;When we have a pointer interact with the screen we will add it to the pointer map (since it can be multi touch) and start tracking the offset.&lt;/p&gt;
&lt;p&gt;The delta is calculated to move the elements but a global offset is used for the canvas background.&lt;/p&gt;
&lt;p&gt;We are setting the pointer capture events so if the mouse is not perfectly on the item it won&apos;t lose tracking.&lt;/p&gt;
&lt;p&gt;Now let&apos;s add methods for moving the canvas and elements by appending the following to the class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;moveCanvas(delta: Offset) {
    this.offset.x += delta.x;
    this.offset.y += delta.y;
    this.root.style.setProperty(&amp;quot;--offset-x&amp;quot;, `${this.offset.x}px`);
    this.root.style.setProperty(&amp;quot;--offset-y&amp;quot;, `${this.offset.y}px`);
}

moveElement(child: SupportedNode, delta: Offset) {
    const getNumber = (key: &amp;quot;--dx&amp;quot; | &amp;quot;--dy&amp;quot;, fallback: number) =&amp;gt; {
      const saved = child.style.getPropertyValue(key);
      if (saved.length &amp;gt; 0) {
        return parseFloat(saved.replace(&amp;quot;px&amp;quot;, &amp;quot;&amp;quot;));
      }
      return fallback;
    };
    const dx = getNumber(&amp;quot;--dx&amp;quot;, 0) + delta.x;
    const dy = getNumber(&amp;quot;--dy&amp;quot;, 0) + delta.y;
    child.style.transform = `translate(${dx}px, ${dy}px)`;
    child.style.setProperty(&amp;quot;--dx&amp;quot;, `${dx}px`);
    child.style.setProperty(&amp;quot;--dy&amp;quot;, `${dy}px`);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the canvas it will set a global offset for the CSS &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/background-position&quot;&gt;&lt;code&gt;background-position&lt;/code&gt;&lt;/a&gt; and update the saved offset.&lt;/p&gt;
&lt;p&gt;For the element we want to transform by the delta so the animation is smooth thanks to hardware acceleration. After the transform it will store the offset as CSS custom properties.&lt;/p&gt;
&lt;p&gt;Now let&apos;s add the event handlers to the canvas and elements by adjusting the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;async firstUpdated() {
    const items = Array.from(this.childNodes);
    let i = 0;
    for (const node of items) {
      if (node instanceof SVGElement || node instanceof HTMLElement) {
        const child = node as SupportedNode;
        child.classList.add(&amp;quot;child&amp;quot;);
        child.style.setProperty(&amp;quot;--layer&amp;quot;, `${i}`);
        this.container.append(child);
        child.addEventListener(&amp;quot;pointerdown&amp;quot;, (e: any) =&amp;gt; {
          this.handleDown(e, &amp;quot;element&amp;quot;);
        });
        child.addEventListener(&amp;quot;pointermove&amp;quot;, (e: any) =&amp;gt; {
          this.handleMove(e, &amp;quot;element&amp;quot;, (delta) =&amp;gt; {
            this.moveElement(child, delta);
          });
        });
        i++;
      }
    }
    this.requestUpdate();
    this.root.addEventListener(&amp;quot;pointerdown&amp;quot;, (e: any) =&amp;gt; {
      this.handleDown(e, &amp;quot;canvas&amp;quot;);
    });
    this.root.addEventListener(&amp;quot;pointermove&amp;quot;, (e: any) =&amp;gt; {
      this.handleMove(e, &amp;quot;canvas&amp;quot;, (delta) =&amp;gt; {
        this.moveCanvas(delta);
        for (const node of Array.from(this.container.children)) {
          if (node instanceof SVGElement || node instanceof HTMLElement) {
            this.moveElement(node, delta);
          }
        }
      });
    });
    this.root.addEventListener(&amp;quot;pointerup&amp;quot;, (e: any) =&amp;gt; {
      this.handleUp(e);
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything should work as expected now and the final code should look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, query } from &amp;quot;lit/decorators.js&amp;quot;;

type DragType = &amp;quot;none&amp;quot; | &amp;quot;canvas&amp;quot; | &amp;quot;element&amp;quot;;
type SupportedNode = HTMLElement | SVGElement;

@customElement(&amp;quot;draggable-dom&amp;quot;)
export class DraggableDOM extends LitElement {
  @query(&amp;quot;main&amp;quot;) root!: HTMLElement;
  @query(&amp;quot;#children&amp;quot;) container!: HTMLElement;
  @query(&amp;quot;canvas&amp;quot;) canvas!: HTMLCanvasElement;
  dragType: DragType = &amp;quot;none&amp;quot;;
  offset: Offset = { x: 0, y: 0 };
  pointerMap: Map&amp;lt;number, PointerData&amp;gt; = new Map();

  static styles = css`
    :host {
      --offset-x: 0;
      --offset-y: 0;
      --grid-background-color: white;
      --grid-color: black;
      --grid-size: 40px;
      --grid-dot-size: 1px;
    }
    main {
      overflow: hidden;
    }
    canvas {
      background-size: var(--grid-size) var(--grid-size);
      background-image: radial-gradient(
        circle,
        var(--grid-color) var(--grid-dot-size),
        var(--grid-background-color) var(--grid-dot-size)
      );
      background-position: var(--offset-x) var(--offset-y);
      z-index: 0;
    }
    .full-size {
      width: 100%;
      height: 100%;
      position: fixed;
    }
    .child {
      --dx: 0px;
      --dy: 0px;
      position: fixed;
      flex-shrink: 1;
      z-index: var(--layer, 0);
      transform: translate(var(--dx), var(--dy));
    }
    @media (prefers-color-scheme: dark) {
      main {
        --grid-background-color: black;
        --grid-color: grey;
      }
    }
  `;

  render() {
    console.log(&amp;quot;render&amp;quot;);
    return html`
      &amp;lt;main class=&amp;quot;full-size&amp;quot;&amp;gt;
        &amp;lt;canvas class=&amp;quot;full-size&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;
        &amp;lt;div id=&amp;quot;children&amp;quot; class=&amp;quot;full-size&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/main&amp;gt;
    `;
  }

  handleDown(event: PointerEvent, type: DragType) {
    if (this.dragType === &amp;quot;none&amp;quot;) {
      event.preventDefault();
      this.dragType = type;
      (event.target as Element).setPointerCapture(event.pointerId);
      this.pointerMap.set(event.pointerId, {
        id: event.pointerId,
        startPos: { x: event.clientX, y: event.clientY },
        currentPos: { x: event.clientX, y: event.clientY },
      });
    }
  }

  handleMove(
    event: PointerEvent,
    type: DragType,
    onMove: (delta: Offset) =&amp;gt; void
  ) {
    if (this.dragType === type) {
      event.preventDefault();
      const saved = this.pointerMap.get(event.pointerId)!;
      const current = { ...saved.currentPos };
      saved.currentPos = { x: event.clientX, y: event.clientY };
      const delta = {
        x: saved.currentPos.x - current.x,
        y: saved.currentPos.y - current.y,
      };
      onMove(delta);
    }
  }

  handleUp(event: PointerEvent) {
    this.dragType = &amp;quot;none&amp;quot;;
    (event.target as Element).releasePointerCapture(event.pointerId);
  }

  moveCanvas(delta: Offset) {
    this.offset.x += delta.x;
    this.offset.y += delta.y;
    this.root.style.setProperty(&amp;quot;--offset-x&amp;quot;, `${this.offset.x}px`);
    this.root.style.setProperty(&amp;quot;--offset-y&amp;quot;, `${this.offset.y}px`);
  }

  moveElement(child: SupportedNode, delta: Offset) {
    const getNumber = (key: &amp;quot;--dx&amp;quot; | &amp;quot;--dy&amp;quot;, fallback: number) =&amp;gt; {
      const saved = child.style.getPropertyValue(key);
      if (saved.length &amp;gt; 0) {
        return parseFloat(saved.replace(&amp;quot;px&amp;quot;, &amp;quot;&amp;quot;));
      }
      return fallback;
    };
    const dx = getNumber(&amp;quot;--dx&amp;quot;, 0) + delta.x;
    const dy = getNumber(&amp;quot;--dy&amp;quot;, 0) + delta.y;
    child.style.transform = `translate(${dx}px, ${dy}px)`;
    child.style.setProperty(&amp;quot;--dx&amp;quot;, `${dx}px`);
    child.style.setProperty(&amp;quot;--dy&amp;quot;, `${dy}px`);
  }

  async firstUpdated() {
    const items = Array.from(this.childNodes);
    let i = 0;
    for (const node of items) {
      if (node instanceof SVGElement || node instanceof HTMLElement) {
        const child = node as SupportedNode;
        child.classList.add(&amp;quot;child&amp;quot;);
        child.style.setProperty(&amp;quot;--layer&amp;quot;, `${i}`);
        this.container.append(child);
        child.addEventListener(&amp;quot;pointerdown&amp;quot;, (e: any) =&amp;gt; {
          this.handleDown(e, &amp;quot;element&amp;quot;);
        });
        child.addEventListener(&amp;quot;pointermove&amp;quot;, (e: any) =&amp;gt; {
          this.handleMove(e, &amp;quot;element&amp;quot;, (delta) =&amp;gt; {
            this.moveElement(child, delta);
          });
        });
        child.setAttribute(&amp;quot;draggable&amp;quot;, &amp;quot;false&amp;quot;);
        i++;
      }
    }
    this.requestUpdate();
    this.root.addEventListener(&amp;quot;pointerdown&amp;quot;, (e: any) =&amp;gt; {
      this.handleDown(e, &amp;quot;canvas&amp;quot;);
    });
    this.root.addEventListener(&amp;quot;pointermove&amp;quot;, (e: any) =&amp;gt; {
      this.handleMove(e, &amp;quot;canvas&amp;quot;, (delta) =&amp;gt; {
        this.moveCanvas(delta);
        for (const node of Array.from(this.container.children)) {
          if (node instanceof SVGElement || node instanceof HTMLElement) {
            this.moveElement(node, delta);
          }
        }
      });
    });
    this.root.addEventListener(&amp;quot;touchstart&amp;quot;, function (e) {
      e.preventDefault();
    });
    this.root.addEventListener(&amp;quot;pointerup&amp;quot;, (e: any) =&amp;gt; {
      this.handleUp(e);
    });
  }
}

interface Offset {
  x: number;
  y: number;
}

interface PointerData {
  id: number;
  startPos: Offset;
  currentPos: Offset;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building with Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;. There is also an example on the Lit playground &lt;a href=&quot;https://lit.dev/playground/#project=W3sibmFtZSI6ImxpdC1jc3MtY2FudmFzLnRzIiwiY29udGVudCI6ImltcG9ydCB7IGh0bWwsIGNzcywgTGl0RWxlbWVudCB9IGZyb20gXCJsaXRcIjtcbmltcG9ydCB7IGN1c3RvbUVsZW1lbnQsIHF1ZXJ5IH0gZnJvbSBcImxpdC9kZWNvcmF0b3JzLmpzXCI7XG5cbnR5cGUgRHJhZ1R5cGUgPSBcIm5vbmVcIiB8IFwiY2FudmFzXCIgfCBcImVsZW1lbnRcIjtcbnR5cGUgU3VwcG9ydGVkTm9kZSA9IEhUTUxFbGVtZW50IHwgU1ZHRWxlbWVudDtcblxuQGN1c3RvbUVsZW1lbnQoXCJjc3MtY2FudmFzXCIpXG5leHBvcnQgY2xhc3MgQ1NTQ2FudmFzIGV4dGVuZHMgTGl0RWxlbWVudCB7XG4gIEBxdWVyeShcIm1haW5cIikgcm9vdCE6IEhUTUxFbGVtZW50O1xuICBAcXVlcnkoXCIjY2hpbGRyZW5cIikgY29udGFpbmVyITogSFRNTEVsZW1lbnQ7XG4gIEBxdWVyeShcImNhbnZhc1wiKSBjYW52YXMhOiBIVE1MQ2FudmFzRWxlbWVudDtcbiAgZHJhZ1R5cGU6IERyYWdUeXBlID0gXCJub25lXCI7XG4gIG9mZnNldDogT2Zmc2V0ID0geyB4OiAwLCB5OiAwIH07XG4gIHBvaW50ZXJNYXA6IE1hcDxudW1iZXIsIFBvaW50ZXJEYXRhPiA9IG5ldyBNYXAoKTtcblxuICBzdGF0aWMgc3R5bGVzID0gY3NzYFxuICAgIDpob3N0IHtcbiAgICAgIC0tb2Zmc2V0LXg6IDA7XG4gICAgICAtLW9mZnNldC15OiAwO1xuICAgICAgLS1ncmlkLWJhY2tncm91bmQtY29sb3I6IHdoaXRlO1xuICAgICAgLS1ncmlkLWNvbG9yOiBibGFjaztcbiAgICAgIC0tZ3JpZC1zaXplOiA0MHB4O1xuICAgICAgLS1ncmlkLWRvdC1zaXplOiAxcHg7XG4gICAgfVxuICAgIG1haW4ge1xuICAgICAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgICB9XG4gICAgY2FudmFzIHtcbiAgICAgIGJhY2tncm91bmQtc2l6ZTogdmFyKC0tZ3JpZC1zaXplKSB2YXIoLS1ncmlkLXNpemUpO1xuICAgICAgYmFja2dyb3VuZC1pbWFnZTogcmFkaWFsLWdyYWRpZW50KFxuICAgICAgICBjaXJjbGUsXG4gICAgICAgIHZhcigtLWdyaWQtY29sb3IpIHZhcigtLWdyaWQtZG90LXNpemUpLFxuICAgICAgICB2YXIoLS1ncmlkLWJhY2tncm91bmQtY29sb3IpIHZhcigtLWdyaWQtZG90LXNpemUpXG4gICAgICApO1xuICAgICAgYmFja2dyb3VuZC1wb3NpdGlvbjogdmFyKC0tb2Zmc2V0LXgpIHZhcigtLW9mZnNldC15KTtcbiAgICAgIHotaW5kZXg6IDA7XG4gICAgfVxuICAgIC5mdWxsLXNpemUge1xuICAgICAgd2lkdGg6IDEwMCU7XG4gICAgICBoZWlnaHQ6IDEwMCU7XG4gICAgICBwb3NpdGlvbjogZml4ZWQ7XG4gICAgfVxuICAgIC5jaGlsZCB7XG4gICAgICAtLWR4OiAwcHg7XG4gICAgICAtLWR5OiAwcHg7XG4gICAgICBwb3NpdGlvbjogZml4ZWQ7XG4gICAgICBmbGV4LXNocmluazogMTtcbiAgICAgIHotaW5kZXg6IHZhcigtLWxheWVyLCAwKTtcbiAgICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlKHZhcigtLWR4KSwgdmFyKC0tZHkpKTtcbiAgICB9XG4gICAgQG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyaykge1xuICAgICAgbWFpbiB7XG4gICAgICAgIC0tZ3JpZC1iYWNrZ3JvdW5kLWNvbG9yOiBibGFjaztcbiAgICAgICAgLS1ncmlkLWNvbG9yOiBncmV5O1xuICAgICAgfVxuICAgIH1cbiAgYDtcblxuICByZW5kZXIoKSB7XG4gICAgY29uc29sZS5sb2coXCJyZW5kZXJcIik7XG4gICAgcmV0dXJuIGh0bWxgXG4gICAgICA8bWFpbiBjbGFzcz1cImZ1bGwtc2l6ZVwiPlxuICAgICAgICA8Y2FudmFzIGNsYXNzPVwiZnVsbC1zaXplXCI-PC9jYW52YXM-XG4gICAgICAgIDxkaXYgaWQ9XCJjaGlsZHJlblwiIGNsYXNzPVwiZnVsbC1zaXplXCI-PC9kaXY-XG4gICAgICA8L21haW4-XG4gICAgYDtcbiAgfVxuXG4gIGhhbmRsZURvd24oZXZlbnQ6IFBvaW50ZXJFdmVudCwgdHlwZTogRHJhZ1R5cGUpIHtcbiAgICBpZiAodGhpcy5kcmFnVHlwZSA9PT0gXCJub25lXCIpIHtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICB0aGlzLmRyYWdUeXBlID0gdHlwZTtcbiAgICAgIChldmVudC50YXJnZXQgYXMgRWxlbWVudCkuc2V0UG9pbnRlckNhcHR1cmUoZXZlbnQucG9pbnRlcklkKTtcbiAgICAgIHRoaXMucG9pbnRlck1hcC5zZXQoZXZlbnQucG9pbnRlcklkLCB7XG4gICAgICAgIGlkOiBldmVudC5wb2ludGVySWQsXG4gICAgICAgIHN0YXJ0UG9zOiB7IHg6IGV2ZW50LmNsaWVudFgsIHk6IGV2ZW50LmNsaWVudFkgfSxcbiAgICAgICAgY3VycmVudFBvczogeyB4OiBldmVudC5jbGllbnRYLCB5OiBldmVudC5jbGllbnRZIH0sXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBoYW5kbGVNb3ZlKFxuICAgIGV2ZW50OiBQb2ludGVyRXZlbnQsXG4gICAgdHlwZTogRHJhZ1R5cGUsXG4gICAgb25Nb3ZlOiAoZGVsdGE6IE9mZnNldCkgPT4gdm9pZFxuICApIHtcbiAgICBpZiAodGhpcy5kcmFnVHlwZSA9PT0gdHlwZSkge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgIGNvbnN0IHNhdmVkID0gdGhpcy5wb2ludGVyTWFwLmdldChldmVudC5wb2ludGVySWQpITtcbiAgICAgIGNvbnN0IGN1cnJlbnQgPSB7IC4uLnNhdmVkLmN1cnJlbnRQb3MgfTtcbiAgICAgIHNhdmVkLmN1cnJlbnRQb3MgPSB7IHg6IGV2ZW50LmNsaWVudFgsIHk6IGV2ZW50LmNsaWVudFkgfTtcbiAgICAgIGNvbnN0IGRlbHRhID0ge1xuICAgICAgICB4OiBzYXZlZC5jdXJyZW50UG9zLnggLSBjdXJyZW50LngsXG4gICAgICAgIHk6IHNhdmVkLmN1cnJlbnRQb3MueSAtIGN1cnJlbnQueSxcbiAgICAgIH07XG4gICAgICBvbk1vdmUoZGVsdGEpO1xuICAgIH1cbiAgfVxuXG4gIGhhbmRsZVVwKGV2ZW50OiBQb2ludGVyRXZlbnQpIHtcbiAgICB0aGlzLmRyYWdUeXBlID0gXCJub25lXCI7XG4gICAgKGV2ZW50LnRhcmdldCBhcyBFbGVtZW50KS5yZWxlYXNlUG9pbnRlckNhcHR1cmUoZXZlbnQucG9pbnRlcklkKTtcbiAgfVxuXG4gIG1vdmVDYW52YXMoZGVsdGE6IE9mZnNldCkge1xuICAgIHRoaXMub2Zmc2V0LnggKz0gZGVsdGEueDtcbiAgICB0aGlzLm9mZnNldC55ICs9IGRlbHRhLnk7XG4gICAgdGhpcy5yb290LnN0eWxlLnNldFByb3BlcnR5KFwiLS1vZmZzZXQteFwiLCBgJHt0aGlzLm9mZnNldC54fXB4YCk7XG4gICAgdGhpcy5yb290LnN0eWxlLnNldFByb3BlcnR5KFwiLS1vZmZzZXQteVwiLCBgJHt0aGlzLm9mZnNldC55fXB4YCk7XG4gIH1cblxuICBtb3ZlRWxlbWVudChjaGlsZDogU3VwcG9ydGVkTm9kZSwgZGVsdGE6IE9mZnNldCkge1xuICAgIGNvbnN0IGdldE51bWJlciA9IChrZXk6IFwiLS1keFwiIHwgXCItLWR5XCIsIGZhbGxiYWNrOiBudW1iZXIpID0-IHtcbiAgICAgIGNvbnN0IHNhdmVkID0gY2hpbGQuc3R5bGUuZ2V0UHJvcGVydHlWYWx1ZShrZXkpO1xuICAgICAgaWYgKHNhdmVkLmxlbmd0aCA-IDApIHtcbiAgICAgICAgcmV0dXJuIHBhcnNlRmxvYXQoc2F2ZWQucmVwbGFjZShcInB4XCIsIFwiXCIpKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxsYmFjaztcbiAgICB9O1xuICAgIGNvbnN0IGR4ID0gZ2V0TnVtYmVyKFwiLS1keFwiLCAwKSArIGRlbHRhLng7XG4gICAgY29uc3QgZHkgPSBnZXROdW1iZXIoXCItLWR5XCIsIDApICsgZGVsdGEueTtcbiAgICBjaGlsZC5zdHlsZS50cmFuc2Zvcm0gPSBgdHJhbnNsYXRlKCR7ZHh9cHgsICR7ZHl9cHgpYDtcbiAgICBjaGlsZC5zdHlsZS5zZXRQcm9wZXJ0eShcIi0tZHhcIiwgYCR7ZHh9cHhgKTtcbiAgICBjaGlsZC5zdHlsZS5zZXRQcm9wZXJ0eShcIi0tZHlcIiwgYCR7ZHl9cHhgKTtcbiAgfVxuXG4gIGFzeW5jIGZpcnN0VXBkYXRlZCgpIHtcbiAgICBjb25zdCBpdGVtcyA9IEFycmF5LmZyb20odGhpcy5jaGlsZE5vZGVzKTtcbiAgICBsZXQgaSA9IDA7XG4gICAgZm9yIChjb25zdCBub2RlIG9mIGl0ZW1zKSB7XG4gICAgICBpZiAobm9kZSBpbnN0YW5jZW9mIFNWR0VsZW1lbnQgfHwgbm9kZSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50KSB7XG4gICAgICAgIGNvbnN0IGNoaWxkID0gbm9kZSBhcyBTdXBwb3J0ZWROb2RlO1xuICAgICAgICBjaGlsZC5jbGFzc0xpc3QuYWRkKFwiY2hpbGRcIik7XG4gICAgICAgIGNoaWxkLnN0eWxlLnNldFByb3BlcnR5KFwiLS1sYXllclwiLCBgJHtpfWApO1xuICAgICAgICB0aGlzLmNvbnRhaW5lci5hcHBlbmQoY2hpbGQpO1xuICAgICAgICBjaGlsZC5hZGRFdmVudExpc3RlbmVyKFwicG9pbnRlcmRvd25cIiwgKGU6IGFueSkgPT4ge1xuICAgICAgICAgIHRoaXMuaGFuZGxlRG93bihlLCBcImVsZW1lbnRcIik7XG4gICAgICAgIH0pO1xuICAgICAgICBjaGlsZC5hZGRFdmVudExpc3RlbmVyKFwicG9pbnRlcm1vdmVcIiwgKGU6IGFueSkgPT4ge1xuICAgICAgICAgIHRoaXMuaGFuZGxlTW92ZShlLCBcImVsZW1lbnRcIiwgKGRlbHRhKSA9PiB7XG4gICAgICAgICAgICB0aGlzLm1vdmVFbGVtZW50KGNoaWxkLCBkZWx0YSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBpKys7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMucmVxdWVzdFVwZGF0ZSgpO1xuICAgIHRoaXMucm9vdC5hZGRFdmVudExpc3RlbmVyKFwicG9pbnRlcmRvd25cIiwgKGU6IGFueSkgPT4ge1xuICAgICAgdGhpcy5oYW5kbGVEb3duKGUsIFwiY2FudmFzXCIpO1xuICAgIH0pO1xuICAgIHRoaXMucm9vdC5hZGRFdmVudExpc3RlbmVyKFwicG9pbnRlcm1vdmVcIiwgKGU6IGFueSkgPT4ge1xuICAgICAgdGhpcy5oYW5kbGVNb3ZlKGUsIFwiY2FudmFzXCIsIChkZWx0YSkgPT4ge1xuICAgICAgICB0aGlzLm1vdmVDYW52YXMoZGVsdGEpO1xuICAgICAgICBmb3IgKGNvbnN0IG5vZGUgb2YgQXJyYXkuZnJvbSh0aGlzLmNvbnRhaW5lci5jaGlsZHJlbikpIHtcbiAgICAgICAgICBpZiAobm9kZSBpbnN0YW5jZW9mIFNWR0VsZW1lbnQgfHwgbm9kZSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50KSB7XG4gICAgICAgICAgICB0aGlzLm1vdmVFbGVtZW50KG5vZGUsIGRlbHRhKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICAgIHRoaXMucm9vdC5hZGRFdmVudExpc3RlbmVyKFwicG9pbnRlcnVwXCIsIChlOiBhbnkpID0-IHtcbiAgICAgIHRoaXMuaGFuZGxlVXAoZSk7XG4gICAgfSk7XG4gIH1cbn1cblxuaW50ZXJmYWNlIE9mZnNldCB7XG4gIHg6IG51bWJlcjtcbiAgeTogbnVtYmVyO1xufVxuXG5pbnRlcmZhY2UgUG9pbnRlckRhdGEge1xuICBpZDogbnVtYmVyO1xuICBzdGFydFBvczogT2Zmc2V0O1xuICBjdXJyZW50UG9zOiBPZmZzZXQ7XG59XG4ifSx7Im5hbWUiOiJpbmRleC5odG1sIiwiY29udGVudCI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI-XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiIC8-XG4gICAgPGxpbmsgcmVsPVwiaWNvblwiIHR5cGU9XCJpbWFnZS9zdmcreG1sXCIgaHJlZj1cIi9zcmMvZmF2aWNvbi5zdmdcIiAvPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCIgLz5cbiAgICA8dGl0bGU-TGl0IENTUyBDYW52YXM8L3RpdGxlPlxuICAgIDxzdHlsZT5cbiAgICAgIGJvZHkge1xuICAgICAgICBtYXJnaW46IDA7XG4gICAgICAgIHBhZGRpbmc6IDA7XG4gICAgICAgIHdpZHRoOiAxMDAlO1xuICAgICAgICBoZWlnaHQ6IDEwMHZoO1xuICAgICAgfVxuICAgIDwvc3R5bGU-XG4gICAgPHNjcmlwdCB0eXBlPVwibW9kdWxlXCIgc3JjPVwiLi9saXQtY3NzLWNhbnZhcy5qc1wiPjwvc2NyaXB0PlxuICA8L2hlYWQ-XG4gIDxib2R5PlxuICAgIDxjc3MtY2FudmFzPlxuICAgICAgPGltZ1xuICAgICAgICBzcmM9XCJodHRwczovL2xpdC5kZXYvaW1hZ2VzL2xvZ28uc3ZnXCJcbiAgICAgICAgYWx0PVwiTGl0IExvZ29cIlxuICAgICAgICB3aWR0aD1cIjUwMFwiXG4gICAgICAgIGhlaWdodD1cIjMzM1wiXG4gICAgICAgIHN0eWxlPVwiLS1keDogNTkuNDkwOXB4OyAtLWR5OiAzMi44NDI5cHhcIlxuICAgICAgLz5cbiAgICAgIDxzdmcgd2lkdGg9XCI0MDBcIiBoZWlnaHQ9XCIxMTBcIiBzdHlsZT1cIi0tZHg6IDIzMC4wNTdweDsgLS1keTogMzMuNjI1N3B4XCI-XG4gICAgICAgIDxyZWN0XG4gICAgICAgICAgd2lkdGg9XCI0MDBcIlxuICAgICAgICAgIGhlaWdodD1cIjEwMFwiXG4gICAgICAgICAgc3R5bGU9XCJmaWxsOiByZ2IoMCwgMCwgMjU1KTsgc3Ryb2tlLXdpZHRoOiAzOyBzdHJva2U6IHJnYigwLCAwLCAwKVwiXG4gICAgICAgIC8-XG4gICAgICA8L3N2Zz5cbiAgICAgIDxzdmcgaGVpZ2h0PVwiMTAwXCIgd2lkdGg9XCIxMDBcIj5cbiAgICAgICAgPGNpcmNsZVxuICAgICAgICAgIGN4PVwiNTBcIlxuICAgICAgICAgIGN5PVwiNTBcIlxuICAgICAgICAgIHI9XCI0MFwiXG4gICAgICAgICAgc3Ryb2tlPVwiYmxhY2tcIlxuICAgICAgICAgIHN0cm9rZS13aWR0aD1cIjNcIlxuICAgICAgICAgIGZpbGw9XCJyZWRcIlxuICAgICAgICAvPlxuICAgICAgPC9zdmc-XG4gICAgPC9jc3MtY2FudmFzPlxuICA8L2JvZHk-XG48L2h0bWw-XG4ifV0&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source for this example can be found &lt;a href=&quot;https://github.com/rodydavis/lit-draggable-dom&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>2D or 3D Force Graph with Lit</title><link>https://rodydavis.com/lit/force-graph/</link><guid isPermaLink="true">https://rodydavis.com/lit/force-graph/</guid><description>Learn to create dynamic 2D or 3D force graphs using Lit in this comprehensive tutorial, including setup instructions, code examples, and a full source repository.</description><pubDate>Sun, 19 Jan 2025 06:03:11 GMT</pubDate><content:encoded>&lt;h1&gt;2D or 3D Force Graph with Lit&lt;/h1&gt;
&lt;p&gt;In this article we will cover how to create a 2D/3D force graph using &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/lit-force-graph&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/lit-force-graph/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;lit-force-graph&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd lit-force-graph force-graph
npm i lit 3d-force-graph
npm i -D @types/node
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &amp;quot;/lit-force-graph/&amp;quot;,
  build: {
    lib: {
      entry: &amp;quot;src/lit-force-graph.ts&amp;quot;,
      formats: [&amp;quot;es&amp;quot;],
    },
    rollupOptions: {
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Lit Force Graph&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/lit-force-graph.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/style.css&amp;quot; /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;lit-force-graph&amp;gt;
      &amp;lt;script type=&amp;quot;application/json&amp;quot;&amp;gt;
        {
          &amp;quot;name&amp;quot;: &amp;quot;Lit Force Graph&amp;quot;,
          &amp;quot;description&amp;quot;: &amp;quot;A force graph built with Lit&amp;quot;,
          &amp;quot;nodes&amp;quot;: [
            {
              &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,
              &amp;quot;name&amp;quot;: &amp;quot;Node 1&amp;quot;
            },
            {
              &amp;quot;id&amp;quot;: &amp;quot;2&amp;quot;,
              &amp;quot;name&amp;quot;: &amp;quot;Node 2&amp;quot;
            },
            {
              &amp;quot;id&amp;quot;: &amp;quot;3&amp;quot;,
              &amp;quot;name&amp;quot;: &amp;quot;Node 3&amp;quot;
            },
            {
              &amp;quot;id&amp;quot;: &amp;quot;4&amp;quot;,
              &amp;quot;name&amp;quot;: &amp;quot;Node 4&amp;quot;
            }
          ],
          &amp;quot;links&amp;quot;: [
            {
              &amp;quot;source&amp;quot;: &amp;quot;1&amp;quot;,
              &amp;quot;target&amp;quot;: &amp;quot;2&amp;quot;
            },
            {
              &amp;quot;source&amp;quot;: &amp;quot;1&amp;quot;,
              &amp;quot;target&amp;quot;: &amp;quot;3&amp;quot;
            },
            {
              &amp;quot;source&amp;quot;: &amp;quot;2&amp;quot;,
              &amp;quot;target&amp;quot;: &amp;quot;3&amp;quot;
            },
            {
              &amp;quot;source&amp;quot;: &amp;quot;2&amp;quot;,
              &amp;quot;target&amp;quot;: &amp;quot;4&amp;quot;
            },
            {
              &amp;quot;source&amp;quot;: &amp;quot;3&amp;quot;,
              &amp;quot;target&amp;quot;: &amp;quot;4&amp;quot;
            },
            {
              &amp;quot;source&amp;quot;: &amp;quot;4&amp;quot;,
              &amp;quot;target&amp;quot;: &amp;quot;1&amp;quot;
            }
          ]
        }
      &amp;lt;/script&amp;gt;
    &amp;lt;/lit-force-graph&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are passing the graph data as JSON here, but we could also set a src attribute pointed to a remote or local file. It is still possible to set the graph data directly on a component.&lt;/p&gt;
&lt;h2&gt;Styles&lt;/h2&gt;
&lt;p&gt;Create and open the &lt;code&gt;public/style.css&lt;/code&gt; file and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  font-size: 12px;
  font-family: sans-serif;
  position: relative;
  width: 100%;
  height: 100%;
}

lit-force-graph {
  width: 100%;
  height: 100vh;
}

:root {
  --graph-background-color: #eee;
  --graph-foreground-color: #000;
  --graph-line-color: rgb(90, 90, 90);
  --graph-node-color: rgb(218, 14, 14);
}

@media (prefers-color-scheme: dark) {
  :root {
    --graph-background-color: #000;
    --graph-foreground-color: #fafafa;
    --graph-line-color: rgb(214, 214, 214);
    --graph-node-color: rgb(228, 8, 8);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Web Component&lt;/h2&gt;
&lt;p&gt;Before we update our component we need to rename &lt;code&gt;my-element.ts&lt;/code&gt; to &lt;code&gt;lit-force-graph.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Open up &lt;code&gt;lit-force-graph.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property, query, state } from &amp;quot;lit/decorators.js&amp;quot;;

export const tagName = &amp;quot;lit-force-graph&amp;quot;;

@customElement(tagName)
export class LitForceGraph extends LitElement {
  static styles = css`
    :host {
      background-color: var(--graph-background-color, #000011);
      color: var(--graph-foreground-color, #ffffff);
      width: var(--graph-width, 100%);
      height: var(--graph-height, 100vh);
    }

    #graph {
      width: 100%;
      height: 100%;
      width: var(--graph-width, 100%);
      height: var(--graph-height, 100vh);
    }

    #controls {
      position: absolute;
      top: 20px;
      right: 20px;
      z-index: 100 !important;
      display: flex;
      flex-direction: column;
      align-items: flex-end;
    }
    #controls div {
      padding: 5px;
    }

    #info {
      position: absolute;
      top: 10px;
      left: 10px;
      z-index: 100 !important;
      display: flex;
      flex-direction: column;
      align-items: flex-start;
    }

    #tooltips {
      position: absolute;
      bottom: 10px;
      left: 10px;
      right: 10px;
      display: flex;
      flex-direction: row;
      align-items: center;
      text-align: center;
      justify-content: center;
    }

    .node-tooltip {
      background-color: var(--graph-foreground-color, #ffffff);
      color: var(--graph-background-color, #000011);
      border-radius: 5px;
      font-size: 12px;
      padding: 5px;
      opacity: 0.67;
    }

    #graph-description {
      opacity: 0.67;
    }

    .scene-tooltip {
      color: var(--graph-foreground-color, #ffffff);
      background-color: transparent;
      display: none;
    }
  `;

  @query(&amp;quot;#graph&amp;quot;) graph!: HTMLElement;
  @property() src = &amp;quot;&amp;quot;;
  @property() mode = &amp;quot;2D&amp;quot;;

  render() {
    return html` &amp;lt;main
      accept=&amp;quot;application/json&amp;quot;
      @drop=&amp;quot;${this.onDrop}&amp;quot;
      @dragover=&amp;quot;${(e: Event) =&amp;gt; e.preventDefault()}&amp;quot;
    &amp;gt;
      &amp;lt;div id=&amp;quot;graph&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;controls&amp;quot;&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;label for=&amp;quot;render-mode&amp;quot;&amp;gt;Render mode&amp;lt;/label&amp;gt;
          &amp;lt;select id=&amp;quot;render-mode&amp;quot; @change=${this.onChangeMode}&amp;gt;
            &amp;lt;!-- TODO: Add render options --&amp;gt;
          &amp;lt;/select&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;info&amp;quot;&amp;gt;
        &amp;lt;!-- TODO: Add labels for graph --&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;tooltips&amp;quot;&amp;gt;
        &amp;lt;!-- TODO: Add tooltip for node --&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/main&amp;gt;`;
  }

  override async firstUpdated() {
    await this.refresh();
    const prefersDark = window.matchMedia(&amp;quot;(prefers-color-scheme: dark)&amp;quot;);
    prefersDark.addEventListener(&amp;quot;change&amp;quot;, () =&amp;gt; {
      this.refresh();
    });
  }

  override attributeChangedCallback(
    name: string,
    _old: string | null,
    value: string | null
  ): void {
    if (name === &amp;quot;src&amp;quot; &amp;amp;&amp;amp; value) {
      this.refresh();
    }
    if (name === &amp;quot;data&amp;quot; &amp;amp;&amp;amp; value) {
      this.setData(JSON.parse(value));
    }
    if (name === &amp;quot;mode&amp;quot; &amp;amp;&amp;amp; value) {
      this.mode = value;
      if (this.data) {
        this.setData({ ...this.data! });
      }
    }
    super.attributeChangedCallback(name, _old, value);
  }

  /**
   * Set the graph data and update the renderer
   *
   * @param data Graph JSON
   */
  setData(data: GraphData) {
    this.data = data;
    // TODO: Render the graph!
  }

  private async refresh() {
    // Get json from script tag
    const children = Array.from(this.children);
    const elem = children.find((child) =&amp;gt; child.tagName === &amp;quot;SCRIPT&amp;quot;);
    if (elem) {
      // Render from script tag contents
      if (elem.textContent) {
        const data = JSON.parse(elem.textContent);
        if (data) this.setData(data);
        // Render from script tag src
      } else if (elem.hasAttribute(&amp;quot;src&amp;quot;)) {
        const url = elem.getAttribute(&amp;quot;src&amp;quot;)!;
        const data = await fetch(url).then((res) =&amp;gt; res.json());
        if (data) this.setData(data);
      }
    } else if (this.src.length &amp;gt; 0) {
      // Render from src attribute
      const data = await fetch(this.src).then((res) =&amp;gt; res.json());
      if (data) this.setData(data);
    }
  }

  private onDrop(e: DragEvent) {
    e.preventDefault();
    const files = e.dataTransfer?.files;
    if (files &amp;amp;&amp;amp; files.length &amp;gt; 0) {
      const file = files[0];
      const reader = new FileReader();
      reader.onload = () =&amp;gt; {
        const json = JSON.parse(reader.result as string);
        this.data = json;
        this.setData(json);
      };
      reader.readAsText(file);
    }
    return false;
  }

  private onChangeMode(e: Event) {
    const mode = (e.target as HTMLSelectElement).value;
    this.mode = mode;
    if (!this.data) return;
    this.setData({ ...this.data! });
  }
}

declare global {
  interface HTMLElementTagNameMap {
    &amp;quot;lit-force-graph&amp;quot;: LitForceGraph;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are creating the base component and wiring it up to listen for a drop event of JSON, accept the src attribute or script tag with json in the text contents.&lt;/p&gt;
&lt;p&gt;The CSS just sets the tooltip at the bottom of the screen, title to the left and the render selection controls to the top right.&lt;/p&gt;
&lt;p&gt;With Lit it makes it easy to support multiple ways to set the data of the component.&lt;/p&gt;
&lt;h3&gt;Inline&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;lit-source-graph&amp;gt;
  &amp;lt;script type=&amp;quot;application/json&amp;quot;&amp;gt;
    {
      &amp;quot;nodes&amp;quot;: [],
      &amp;quot;links&amp;quot;: []
    }
  &amp;lt;/script&amp;gt;
&amp;lt;/lit-source-graph&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Lazy Loading&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;lit-source-graph&amp;gt;&amp;lt;/lit-source-graph&amp;gt;
&amp;lt;script&amp;gt;
  const elem = document.createElement(&amp;quot;lit-source-graph&amp;quot;);
  elem.src = &amp;quot;./graph-data.json&amp;quot;;
  // Or remote url
  elem.src = &amp;quot;https://example.com/graph-data.json&amp;quot;;
  // Or data from an object
  elem.data = { node: [], links: [] };
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Graph Data&lt;/h2&gt;
&lt;p&gt;Create and open the file &lt;code&gt;src/classes/graph.ts&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export class Graph {
  private ids = new Set();
  private graph: GraphData = {
    nodes: [],
    links: [],
  };

  addNode&amp;lt;T = any&amp;gt;(node: GraphNode&amp;lt;T&amp;gt;) {
    if (this.ids.has(node.id)) {
      return this.graph.nodes.find((n) =&amp;gt; n.id === node.id)!;
    }
    this.ids.add(node.id);
    this.graph.nodes.push(node);
    return node;
  }

  addLink&amp;lt;T = any&amp;gt;(link: GraphLink&amp;lt;T&amp;gt;) {
    this.graph.links.push(link);
    return link;
  }

  toJSON() {
    return this.graph;
  }
}

export interface GraphNode&amp;lt;T = any&amp;gt; {
  id: string;
  name?: string;
  group?: string;
  value?: T;
}

export interface GraphLink&amp;lt;T = any&amp;gt; {
  source: string;
  target: string;
  name?: string;
  value?: T;
}

export interface GraphData&amp;lt;A = any, B = any&amp;gt; {
  name?: string;
  description?: string;
  nodes: GraphNode&amp;lt;A&amp;gt;[];
  links: GraphLink&amp;lt;B&amp;gt;[];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are creating a utility class that can generate the nodes and links while excluding duplicates and returning the graph data.&lt;/p&gt;
&lt;p&gt;Create and open the file &lt;code&gt;src/classes/context.ts&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { GraphData, GraphNode } from &amp;quot;./graph&amp;quot;;

export interface RenderContext {
  data: GraphData;
  element: HTMLElement;
  onHover: (node?: GraphNode) =&amp;gt; void;
}

export type Renderer = (context: RenderContext) =&amp;gt; void;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the context type that we will use to create the renderers and pass with the data.&lt;/p&gt;
&lt;h2&gt;2D Renderer&lt;/h2&gt;
&lt;p&gt;Create and open the file &lt;code&gt;src/renderers/mode-2d.ts&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import ForceGraph from &amp;quot;force-graph&amp;quot;;
import { RenderContext } from &amp;quot;../classes/context&amp;quot;;

export function render(context: RenderContext) {
  const graph = ForceGraph();
  const style = getComputedStyle(context.element);
  const lineColor = style.getPropertyValue(&amp;quot;--graph-line-color&amp;quot;).trim();
  const bgColor = style.getPropertyValue(&amp;quot;--graph-background-color&amp;quot;).trim();
  const fgColor = style.getPropertyValue(&amp;quot;--graph-foreground-color&amp;quot;).trim();
  const nodeColor = style.getPropertyValue(&amp;quot;--graph-node-color&amp;quot;).trim();
  graph(context.element)
    .graphData(context.data)
    .width(Number(style.width.slice(0, -2)))
    .height(Number(style.height.slice(0, -2)))
    .cooldownTicks(100)
    .backgroundColor(bgColor)
    .linkColor(() =&amp;gt; lineColor)
    .linkWidth(0.2)
    .nodeCanvasObject((node: any, ctx, globalScale) =&amp;gt; {
      // Draw a circle
      ctx.beginPath();
      const size = 5 / globalScale;
      ctx.arc(node.x, node.y, size, 0, 2 * Math.PI);
      //   ctx.fillStyle = nodeColor(node, groupColors);
      ctx.fillStyle = nodeColor;
      ctx.fill();
      ctx.lineWidth = 1 / globalScale;
      ctx.strokeStyle = lineColor;
      ctx.stroke();

      if (globalScale &amp;gt;= 4) {
        const label = node.name ?? node.id;
        const fontSize = 12 / globalScale;
        ctx.font = `${fontSize}px Sans-Serif`;
        const textWidth = ctx.measureText(label).width;
        const bckgDimensions = [textWidth, fontSize].map(
          (n) =&amp;gt; n + fontSize * 0.2
        ); // some padding

        ctx.textAlign = &amp;quot;center&amp;quot;;
        ctx.textBaseline = &amp;quot;middle&amp;quot;;
        ctx.fillStyle = fgColor;
        // Measure text
        ctx.fillText(label, node.x + size * 2 + textWidth / 2, node.y);

        node.__bckgDimensions = bckgDimensions;
      }
    })
    .onNodeHover((node: any, prev: any) =&amp;gt; {
      if (node) {
        const graphNode = context.data.nodes.find((n) =&amp;gt; n.id === node.id);
        context.onHover(graphNode);
      }
      if (prev) {
        context.onHover(undefined);
      }
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are importing the context and creating the boilerplate for the 2D renderer. When the scale is greater than 4 we draw the node name to add a little more detail.&lt;/p&gt;
&lt;p&gt;Notice that on node hover we are calling the onHover callback with the hovered node and we are using custom properties to render the colors.&lt;/p&gt;
&lt;h2&gt;3D Renderer&lt;/h2&gt;
&lt;p&gt;Create and open the file &lt;code&gt;src/renderers/mode-3d.ts&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import ForceGraph from &amp;quot;3d-force-graph&amp;quot;;
import { RenderContext } from &amp;quot;../classes/context.js&amp;quot;;

export function render(context: RenderContext) {
  const graph = ForceGraph({
    controlType: &amp;quot;trackball&amp;quot;,
    rendererConfig: { antialias: true, alpha: true },
  });
  const style = getComputedStyle(context.element);
  const lineColor = style.getPropertyValue(&amp;quot;--graph-line-color&amp;quot;).trim();
  const bgColor = style.getPropertyValue(&amp;quot;--graph-background-color&amp;quot;).trim();
  const nodeColor = style.getPropertyValue(&amp;quot;--graph-node-color&amp;quot;).trim();
  graph(context.element)
    .graphData(context.data)
    .width(Number(style.width.slice(0, -2)))
    .height(Number(style.height.slice(0, -2)))
    .showNavInfo(false)
    .linkColor(() =&amp;gt; lineColor)
    .backgroundColor(bgColor)
    .nodeThreeObject((node: any) =&amp;gt; {
      const color = node.color ?? nodeColor;
      node.color = color;
      return false as any;
    })
    .nodeThreeObjectExtend(true)
    .onNodeHover((node: any, prev: any) =&amp;gt; {
      if (node) {
        const graphNode = context.data.nodes.find((n) =&amp;gt; n.id === node.id);
        context.onHover(graphNode);
      }
      if (prev) {
        context.onHover(undefined);
      }
    })
    .cooldownTicks(100);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are almost doing the same thing as the 2D renderer but creating it with &lt;a href=&quot;https://threejs.org/&quot;&gt;Three.js&lt;/a&gt; instead.&lt;/p&gt;
&lt;h2&gt;Rendering&lt;/h2&gt;
&lt;p&gt;Now open up &lt;code&gt;src/lit-force-graph.ts&lt;/code&gt; and the imports for the renderers and graph/context classes we created:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// ...
import { Renderer } from &amp;quot;./classes/context&amp;quot;;
import { GraphData, GraphNode } from &amp;quot;./classes/graph&amp;quot;;
import { render as render2D } from &amp;quot;./modes/mode-2d&amp;quot;;
import { render as render3D } from &amp;quot;./modes/mode-3d&amp;quot;;
// ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now add the property for the graph data and the renderers in the class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;  @property({ type: Object }) data?: GraphData;
  @state() hovered?: GraphNode;

  renderers = new Map&amp;lt;string, Renderer&amp;gt;([
    [&amp;quot;2D&amp;quot;, render2D],
    [&amp;quot;3D&amp;quot;, render3D],
  ]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update &lt;code&gt;setData&lt;/code&gt; to render with the current renderer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;setData(data: GraphData) {
    this.data = data;
    const renderer = this.renderers.get(this.mode);
    renderer?.({
        element: this.graph,
        data,
        onHover: (node) =&amp;gt; (this.hovered = node),
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally update the render method to show the graph title and currently hovered node:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;render() {
    return html` &amp;lt;main
      accept=&amp;quot;application/json&amp;quot;
      @drop=&amp;quot;${this.onDrop}&amp;quot;
      @dragover=&amp;quot;${(e: Event) =&amp;gt; e.preventDefault()}&amp;quot;
    &amp;gt;
      &amp;lt;div id=&amp;quot;graph&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;controls&amp;quot;&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;label for=&amp;quot;render-mode&amp;quot;&amp;gt;Render mode&amp;lt;/label&amp;gt;
          &amp;lt;select id=&amp;quot;render-mode&amp;quot; @change=${this.onChangeMode}&amp;gt;
            ${Array.from(this.renderers.keys()).map((mode) =&amp;gt; {
              return html` &amp;lt;option value=&amp;quot;${mode}&amp;quot;&amp;gt;${mode}&amp;lt;/option&amp;gt; `;
            })}
          &amp;lt;/select&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;info&amp;quot;&amp;gt;
        &amp;lt;h2 id=&amp;quot;graph-name&amp;quot;&amp;gt;${this.data?.name}&amp;lt;/h2&amp;gt;
        &amp;lt;div id=&amp;quot;graph-description&amp;quot;&amp;gt;${this?.data?.description}&amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;tooltips&amp;quot;&amp;gt;
        ${this.hovered
          ? html` &amp;lt;div class=&amp;quot;node-tooltip&amp;quot;&amp;gt;
              ${this.hovered?.name ?? this.hovered?.id}
            &amp;lt;/div&amp;gt;`
          : html``}
      &amp;lt;/div&amp;gt;
    &amp;lt;/main&amp;gt;`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Final Code&lt;/h2&gt;
&lt;p&gt;If everything was added correctly it should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property, query, state } from &amp;quot;lit/decorators.js&amp;quot;;
import { Renderer } from &amp;quot;./classes/context&amp;quot;;
import { GraphData, GraphNode } from &amp;quot;./classes/graph&amp;quot;;
import { render as render2D } from &amp;quot;./modes/mode-2d&amp;quot;;
import { render as render3D } from &amp;quot;./modes/mode-3d&amp;quot;;

export const tagName = &amp;quot;lit-force-graph&amp;quot;;

@customElement(tagName)
export class LitForceGraph extends LitElement {
  static styles = css`
    :host {
      background-color: var(--graph-background-color, #000011);
      color: var(--graph-foreground-color, #ffffff);
      width: var(--graph-width, 100%);
      height: var(--graph-height, 100vh);
    }

    #graph {
      width: 100%;
      height: 100%;
      width: var(--graph-width, 100%);
      height: var(--graph-height, 100vh);
    }

    #controls {
      position: absolute;
      top: 20px;
      right: 20px;
      z-index: 100 !important;
      display: flex;
      flex-direction: column;
      align-items: flex-end;
    }
    #controls div {
      padding: 5px;
    }

    #info {
      position: absolute;
      top: 10px;
      left: 10px;
      z-index: 100 !important;
      display: flex;
      flex-direction: column;
      align-items: flex-start;
    }

    #tooltips {
      position: absolute;
      bottom: 10px;
      left: 10px;
      right: 10px;
      display: flex;
      flex-direction: row;
      align-items: center;
      text-align: center;
      justify-content: center;
    }

    .node-tooltip {
      background-color: var(--graph-foreground-color, #ffffff);
      color: var(--graph-background-color, #000011);
      border-radius: 5px;
      font-size: 12px;
      padding: 5px;
      opacity: 0.67;
    }

    #graph-description {
      opacity: 0.67;
    }

    .scene-tooltip {
      color: var(--graph-foreground-color, #ffffff);
      background-color: transparent;
      display: none;
    }
  `;

  @query(&amp;quot;#graph&amp;quot;) graph!: HTMLElement;
  @property() src = &amp;quot;&amp;quot;;
  @property() mode = &amp;quot;2D&amp;quot;;
  @property({ type: Object }) data?: GraphData;
  @state() hovered?: GraphNode;

  renderers = new Map&amp;lt;string, Renderer&amp;gt;([
    [&amp;quot;2D&amp;quot;, render2D],
    [&amp;quot;3D&amp;quot;, render3D],
  ]);

  render() {
    return html` &amp;lt;main
      accept=&amp;quot;application/json&amp;quot;
      @drop=&amp;quot;${this.onDrop}&amp;quot;
      @dragover=&amp;quot;${(e: Event) =&amp;gt; e.preventDefault()}&amp;quot;
    &amp;gt;
      &amp;lt;div id=&amp;quot;graph&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;controls&amp;quot;&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;label for=&amp;quot;render-mode&amp;quot;&amp;gt;Render mode&amp;lt;/label&amp;gt;
          &amp;lt;select id=&amp;quot;render-mode&amp;quot; @change=${this.onChangeMode}&amp;gt;
            ${Array.from(this.renderers.keys()).map((mode) =&amp;gt; {
              return html` &amp;lt;option value=&amp;quot;${mode}&amp;quot;&amp;gt;${mode}&amp;lt;/option&amp;gt; `;
            })}
          &amp;lt;/select&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;info&amp;quot;&amp;gt;
        &amp;lt;h2 id=&amp;quot;graph-name&amp;quot;&amp;gt;${this.data?.name}&amp;lt;/h2&amp;gt;
        &amp;lt;div id=&amp;quot;graph-description&amp;quot;&amp;gt;${this?.data?.description}&amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;tooltips&amp;quot;&amp;gt;
        ${this.hovered
          ? html` &amp;lt;div class=&amp;quot;node-tooltip&amp;quot;&amp;gt;
              ${this.hovered?.name ?? this.hovered?.id}
            &amp;lt;/div&amp;gt;`
          : html``}
      &amp;lt;/div&amp;gt;
    &amp;lt;/main&amp;gt;`;
  }

  async firstUpdated() {
    await this.refresh();
    const prefersDark = window.matchMedia(&amp;quot;(prefers-color-scheme: dark)&amp;quot;);
    prefersDark.addEventListener(&amp;quot;change&amp;quot;, () =&amp;gt; {
      this.refresh();
    });
  }

  /**
   * Set the graph data and update the renderer
   *
   * @param data Graph JSON
   */
  setData(data: GraphData) {
    this.data = data;
    const renderer = this.renderers.get(this.mode);
    renderer?.({
      element: this.graph,
      data,
      onHover: (node) =&amp;gt; (this.hovered = node),
    });
  }

  private async refresh() {
    // Get json from script tag
    const children = Array.from(this.children);
    const elem = children.find((child) =&amp;gt; child.tagName === &amp;quot;SCRIPT&amp;quot;);
    if (elem) {
      // Render from script tag contents
      if (elem.textContent) {
        const data = JSON.parse(elem.textContent);
        if (data) this.setData(data);
        // Render from script tag src
      } else if (elem.hasAttribute(&amp;quot;src&amp;quot;)) {
        const url = elem.getAttribute(&amp;quot;src&amp;quot;)!;
        const data = await fetch(url).then((res) =&amp;gt; res.json());
        if (data) this.setData(data);
      }
    } else if (this.src.length &amp;gt; 0) {
      // Render from src attribute
      const data = await fetch(this.src).then((res) =&amp;gt; res.json());
      if (data) this.setData(data);
    }
  }

  private onChangeMode(e: Event) {
    const mode = (e.target as HTMLSelectElement).value;
    this.mode = mode;
    if (!this.data) return;
    this.setData({ ...this.data! });
  }

  private onDrop(e: DragEvent) {
    e.preventDefault();
    const files = e.dataTransfer?.files;
    if (files &amp;amp;&amp;amp; files.length &amp;gt; 0) {
      const file = files[0];
      const reader = new FileReader();
      reader.onload = () =&amp;gt; {
        const json = JSON.parse(reader.result as string);
        this.data = json;
        this.setData(json);
      };
      reader.readAsText(file);
    }
    return false;
  }

  attributeChangedCallback(
    name: string,
    _old: string | null,
    value: string | null
  ): void {
    if (name === &amp;quot;src&amp;quot; &amp;amp;&amp;amp; value) {
      this.refresh();
    }
    if (name === &amp;quot;data&amp;quot; &amp;amp;&amp;amp; value) {
      this.setData(JSON.parse(value));
    }
    if (name === &amp;quot;mode&amp;quot; &amp;amp;&amp;amp; value) {
      this.mode = value;
      if (this.data) {
        this.setData({ ...this.data! });
      }
    }
    super.attributeChangedCallback(name, _old, value);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    &amp;quot;lit-force-graph&amp;quot;: LitForceGraph;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2D Light:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/graph_light_hsa07drqll.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2D Dark:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/graph_dark_s951yas96p.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3D Light:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/graph_light_3d_1n3rbzomqr.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3D Dark:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/graph_dark_3d_kckpdzz3lx.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you can render the complex data structures with ease using web components!&lt;/p&gt;
&lt;p&gt;If you want to learn more about building with Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source for this example can be found &lt;a href=&quot;https://github.com/rodydavis/lit-force-graph&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Building A Piano with Flutter</title><link>https://rodydavis.com/lit/making-a-piano/</link><guid isPermaLink="true">https://rodydavis.com/lit/making-a-piano/</guid><description>Build a functional piano application using Flutter, featuring minimal Dart code and detailed step-by-step instructions for setting up dependencies and sound fonts.</description><pubDate>Sun, 19 Jan 2025 06:48:38 GMT</pubDate><content:encoded>&lt;h1&gt;Building A Piano with Flutter&lt;/h1&gt;
&lt;p&gt;This piano uses only &lt;code&gt;5032&lt;/code&gt; bytes of Dart Code!&lt;/p&gt;
&lt;p&gt;Winner of the &lt;a href=&quot;https://flutter.dev/create&quot;&gt;Flutter Create Contest&lt;/a&gt; and you can see the certificate &lt;a href=&quot;https://www.credential.net/exbvca0q?key=8be94f32ad2f56882045e013e960fa888afa4edd52edb963c48df351c7d1e443&quot;&gt;here&lt;/a&gt;!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/flutter_piano/tree/5k&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://pocketpiano.app/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;What you need&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://flutter.dev/docs/get-started/install&quot;&gt;Flutter SDK&lt;/a&gt; Installed (&lt;a href=&quot;https://flutter.dev/docs/get-started/codelab&quot;&gt;More Info&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;.sf2&lt;/code&gt; SoundFont File like &lt;a href=&quot;https://github.com/rodydavis/flutter_piano/blob/5k/assets/sounds/Piano.sf2&quot;&gt;this one&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Physical iOS device (iOS Simulator does not work with this plugin for playing the sounds) or Android Emulator/Device&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Setting Up&lt;/h2&gt;
&lt;p&gt;You can either create a new project with Android Studio or VSCode using the GUI or navigate to the location you want your project and using this command in the terminal: &lt;code&gt;flutter create -i swift -a kotlin flutter_piano&lt;/code&gt;. Make sure to include Swift and Kotlin Support!&lt;/p&gt;
&lt;p&gt;Now that you have your project created it should look like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_1_0o63uvabr3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s start by adding some dependencies to our &lt;code&gt;pubspec.yaml&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;dependencies:
  flutter:
    sdk: flutter
  tonic: ^0.2.3
  flutter_midi: ^0.1.1+3
  cupertino_icons: ^0.1.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and add the .sf2 file&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt; assets:
   - assets/sounds/Piano.sf2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you haven&apos;t already create a new folder at the top of your project call &lt;code&gt;assets&lt;/code&gt; and a subfolder called &lt;code&gt;sounds&lt;/code&gt; and place the .sf2 file there and make sure it is named &lt;code&gt;Piano.sf2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Because our app will only work in landscape we need to update those settings as well.&lt;/p&gt;
&lt;p&gt;navigate to the &lt;code&gt;/android/app/src/main/AndroidManifest.xml&lt;/code&gt; and add this line inside &lt;code&gt;&amp;lt;activity&lt;/code&gt; in the &lt;code&gt;&amp;lt;application&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;android:screenOrientation=&amp;quot;landscape&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;manifest xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;
    package=&amp;quot;com.rodydavis.flutter_piano&amp;quot;&amp;gt;

    &amp;lt;!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. --&amp;gt;
    &amp;lt;application
        android:name=&amp;quot;io.flutter.app.FlutterApplication&amp;quot;
        android:label=&amp;quot;flutter_piano&amp;quot;
        android:icon=&amp;quot;@mipmap/ic_launcher&amp;quot;&amp;gt;
        &amp;lt;activity
            android:name=&amp;quot;.MainActivity&amp;quot;
            android:launchMode=&amp;quot;singleTop&amp;quot;
            android:theme=&amp;quot;@style/LaunchTheme&amp;quot;
            android:configChanges=&amp;quot;orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode&amp;quot;
            android:hardwareAccelerated=&amp;quot;true&amp;quot;
            android:screenOrientation=&amp;quot;landscape&amp;quot;
            android:windowSoftInputMode=&amp;quot;adjustResize&amp;quot;&amp;gt;
            &amp;lt;!-- This keeps the window background of the activity showing
                 until Flutter renders its first frame. It can be removed if
                 there is no splash screen (such as the default splash screen
                 defined in @style/LaunchTheme). --&amp;gt;
            &amp;lt;meta-data
                android:name=&amp;quot;io.flutter.app.android.SplashScreenUntilFirstFrame&amp;quot;
                android:value=&amp;quot;true&amp;quot; /&amp;gt;
            &amp;lt;intent-filter&amp;gt;
                &amp;lt;action android:name=&amp;quot;android.intent.action.MAIN&amp;quot;/&amp;gt;
                &amp;lt;category android:name=&amp;quot;android.intent.category.LAUNCHER&amp;quot;/&amp;gt;
            &amp;lt;/intent-filter&amp;gt;
        &amp;lt;/activity&amp;gt;
    &amp;lt;/application&amp;gt;
&amp;lt;/manifest&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Navigate to &lt;code&gt;/ios/Runner/info.plist&lt;/code&gt; and change:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;key&amp;gt;UISupportedInterfaceOrientations&amp;lt;/key&amp;gt;
&amp;lt;array&amp;gt;
    &amp;lt;string&amp;gt;UIInterfaceOrientationLandscapeLeft&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;UIInterfaceOrientationLandscapeRight&amp;lt;/string&amp;gt;
&amp;lt;/array&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can start with the UI! When you run the application now it should start in landscape!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_2_mhactwtphn.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Step 1&lt;/h2&gt;
&lt;p&gt;To make it eaiser to read lets remove the comments. Use &amp;quot;find and replace&amp;quot; and search for &lt;code&gt;\/\/.*&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;&lt;img src=&quot;../../../assets/piano_3_46kmsg9amc.webp&quot; alt=&quot;&quot;&gt;&lt;/h2&gt;
&lt;p&gt;Choose the &amp;quot;select all occurrances&amp;quot; button and hit &lt;code&gt;backspace&lt;/code&gt; to delete.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_4_de46u35luo.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Hit save and you should see the code format for you.&lt;/p&gt;
&lt;p&gt;The &apos;main.dart&apos; file should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: &apos;Flutter Demo Home Page&apos;),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            Text(
              &apos;You have pushed the button this many times:&apos;,
            ),
            Text(
              &apos;$_counter&apos;,
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: Icon(Icons.add),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2&lt;/h2&gt;
&lt;p&gt;Delete the &lt;code&gt;MyHomePage&lt;/code&gt; widget so you are left with this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: &apos;Flutter Demo Home Page&apos;),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should get an error and thats ok, we will fix that next.&lt;/p&gt;
&lt;p&gt;Replace &lt;code&gt;MyHomePage(title: &apos;Flutter Demo Home Page&apos;)&lt;/code&gt; with a &lt;code&gt;Scaffold()&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 3&lt;/h2&gt;
&lt;p&gt;Change &lt;code&gt;MyApp&lt;/code&gt; to a &lt;code&gt;StatefulWidget&lt;/code&gt;. You can do this quickly by selecting &lt;code&gt;MyApp&lt;/code&gt; and choose &amp;quot;Convert to StatefulWidget&amp;quot; with the helper.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_5_jila6g9dng.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It should look like this now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change the theme to dark. You can do this by setting the &lt;code&gt;ThemeData&lt;/code&gt; in &lt;code&gt;MaterialApp&lt;/code&gt; change&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;theme: ThemeData(
    primarySwatch: Colors.blue,
),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;theme: ThemeData.dark(),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and and &lt;code&gt;AppBar&lt;/code&gt; to the &lt;code&gt;Scaffold&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;appBar: AppBar(title: Text(&amp;quot;Flutter Piano&amp;quot;)),&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData.dark(),
      home: Scaffold(
        appBar: AppBar(title: Text(&amp;quot;Flutter Piano&amp;quot;)),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now build and run your app, it should look like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_6_keahhzsyf2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Step 5&lt;/h2&gt;
&lt;p&gt;We need to add some imports to the top:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/services.dart&apos;;
import &apos;package:flutter_midi/flutter_midi.dart&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you get an error make sure they are added in the &lt;code&gt;pubspec.yaml&lt;/code&gt; from earlier, then restart the app. Be sure to run &lt;code&gt;flutter packages get&lt;/code&gt; everytime you add a dependency.&lt;/p&gt;
&lt;p&gt;Now we can add out &lt;code&gt;initState()&lt;/code&gt; to our app.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; @override
 initState() {
   FlutterMidi.unmute();
   rootBundle.load(&amp;quot;assets/sounds/Piano.sf2&amp;quot;).then((sf2) {
     FlutterMidi.prepare(sf2: sf2, name: &amp;quot;Piano.sf2&amp;quot;);
   });
   super.initState();
 }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the app and make sure you do not get any errors. If you are running this on the iOS Simulator you will get the following error:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Could Not Load Midi on this Device. (Cannot run on simulator), have you included the sound font?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is ok for developing the UI but once we start with the midi you will need to plug in a real device.&lt;/p&gt;
&lt;p&gt;Your code so far should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter/services.dart&apos;;
import &apos;package:flutter_midi/flutter_midi.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  @override
  initState() {
    FlutterMidi.unmute();
    rootBundle.load(&amp;quot;assets/sounds/Piano.sf2&amp;quot;).then((sf2) {
      FlutterMidi.prepare(sf2: sf2, name: &amp;quot;Piano.sf2&amp;quot;);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData.dark(),
      home: Scaffold(
        appBar: AppBar(title: Text(&amp;quot;Flutter Piano&amp;quot;)),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 6&lt;/h2&gt;
&lt;p&gt;To make Flutter development faster we start with containers and colors so we can make sure everything is the right size.&lt;/p&gt;
&lt;p&gt;Lets start by adding a &lt;code&gt;Drawer&lt;/code&gt; with a &lt;code&gt;ListView&lt;/code&gt; to our &lt;code&gt;Scaffold&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; home: Scaffold(
        appBar: AppBar(title: Text(&amp;quot;Flutter Piano&amp;quot;)),
        drawer: Drawer(child: SafeArea(child: ListView(children: &amp;lt;Widget&amp;gt;[]))),
      ),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now get a menu icon that when you press looks like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_7_emhzhly4r3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now lets add a ListView that scrolls Horizontially to the body of the &lt;code&gt;Scaffold&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; body: ListView.builder(
            itemCount: 7,
            scrollDirection: Axis.horizontal,
            itemBuilder: (BuildContext context, int index) {
              return Container();
            },
          )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need 7 &lt;code&gt;itemCount&lt;/code&gt; for 7 octaves on the Piano.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter/services.dart&apos;;
import &apos;package:flutter_midi/flutter_midi.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  @override
  initState() {
    FlutterMidi.unmute();
    rootBundle.load(&amp;quot;assets/sounds/Piano.sf2&amp;quot;).then((sf2) {
      FlutterMidi.prepare(sf2: sf2, name: &amp;quot;Piano.sf2&amp;quot;);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData.dark(),
      home: Scaffold(
          appBar: AppBar(title: Text(&amp;quot;Flutter Piano&amp;quot;)),
          drawer:
              Drawer(child: SafeArea(child: ListView(children: &amp;lt;Widget&amp;gt;[]))),
          body: ListView.builder(
            itemCount: 7,
            scrollDirection: Axis.horizontal,
            itemBuilder: (BuildContext context, int index) {
              return Container();
            },
          )),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 7&lt;/h2&gt;
&lt;p&gt;Now we need to build the octave section that will be repeated. Since every octave is identical we can repeat the octaves with minor adjustments.&lt;/p&gt;
&lt;p&gt;Lets add some parameters for use to define for our UI. Add these underneath the initState function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;double get keyWidth =&amp;gt; 80 + (80 * _widthRatio);
double _widthRatio = 0.0;
bool _showLabels = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will use these to dynamily update the keys.&lt;/p&gt;
&lt;p&gt;Under the &lt;code&gt;itemBuilder&lt;/code&gt; lets define which octave we are working with by adding:&lt;br&gt;
&lt;code&gt;final int i = index * 12;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Our code should look like this now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter/services.dart&apos;;
import &apos;package:flutter_midi/flutter_midi.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  @override
  initState() {
    FlutterMidi.unmute();
    rootBundle.load(&amp;quot;assets/sounds/Piano.sf2&amp;quot;).then((sf2) {
      FlutterMidi.prepare(sf2: sf2, name: &amp;quot;Piano.sf2&amp;quot;);
    });
    super.initState();
  }
  
  double get keyWidth =&amp;gt; 80 + (80 * _widthRatio);
  double _widthRatio = 0.0;
  bool _showLabels = true;
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData.dark(),
      home: Scaffold(
          appBar: AppBar(title: Text(&amp;quot;Flutter Piano&amp;quot;)),
          drawer:
              Drawer(child: SafeArea(child: ListView(children: &amp;lt;Widget&amp;gt;[]))),
          body: ListView.builder(
            itemCount: 7,
            scrollDirection: Axis.horizontal,
            itemBuilder: (BuildContext context, int index) {
              final int i = index * 12;
              return Container();
            },
          )),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 8&lt;/h2&gt;
&lt;p&gt;Now we need to add a &lt;code&gt;Stack&lt;/code&gt; for our octave:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; return SafeArea(
               child: Stack(children: &amp;lt;Widget&amp;gt;[
                 Row(mainAxisSize: MainAxisSize.min, children: &amp;lt;Widget&amp;gt;[
                   _buildKey(24 + i, false),
                   _buildKey(26 + i, false),
                   _buildKey(28 + i, false),
                   _buildKey(29 + i, false),
                   _buildKey(31 + i, false),
                   _buildKey(33 + i, false),
                   _buildKey(35 + i, false),
                 ]),
                 Positioned(
                     left: 0.0,
                     right: 0.0,
                     bottom: 100,
                     top: 0.0,
                     child: Row(
                         mainAxisAlignment: MainAxisAlignment.spaceBetween,
                         mainAxisSize: MainAxisSize.min,
                         children: &amp;lt;Widget&amp;gt;[
                           Container(width: keyWidth * .5),
                           _buildKey(25 + i, true),
                           _buildKey(27 + i, true),
                           Container(width: keyWidth),
                           _buildKey(30 + i, true),
                           _buildKey(32 + i, true),
                           _buildKey(34 + i, true),
                           Container(width: keyWidth * .5),
                         ])),
               ]),
             );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we have defined which midi notes are played for each octave.&lt;/p&gt;
&lt;p&gt;Now add the function &lt;code&gt;_buildKey&lt;/code&gt; underneath our &lt;code&gt;build&lt;/code&gt; function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; Widget _buildKey(int midi, bool accidental) {
     if (accidental) {
      return Container(
          width: keyWidth,
          color: Colors.black,
          margin: EdgeInsets.symmetric(horizontal: 2.0),
          padding: EdgeInsets.symmetric(horizontal: keyWidth * .1),
          child: Material(
            elevation: 6.0,
            borderRadius: borderRadius,
            shadowColor: Color(0x802196F3),
          ));
    }
    return Container(
        width: keyWidth,
        color: Colors.white,
        margin: EdgeInsets.symmetric(horizontal: 2.0));
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also add &lt;code&gt;borderRadius&lt;/code&gt; to the bottom of &lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;const BorderRadiusGeometry borderRadius = BorderRadius.only(
    bottomLeft: Radius.circular(10.0), bottomRight: Radius.circular(10.0));

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your app should look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_8_jie1vsmdtx.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Your code should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter/services.dart&apos;;
import &apos;package:flutter_midi/flutter_midi.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  @override
  initState() {
    FlutterMidi.unmute();
    rootBundle.load(&amp;quot;assets/sounds/Piano.sf2&amp;quot;).then((sf2) {
      FlutterMidi.prepare(sf2: sf2, name: &amp;quot;Piano.sf2&amp;quot;);
    });
    super.initState();
  }

  double get keyWidth =&amp;gt; 80 + (80 * _widthRatio);
  double _widthRatio = 0.0;
  bool _showLabels = true;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData.dark(),
      home: Scaffold(
          appBar: AppBar(title: Text(&amp;quot;Flutter Piano&amp;quot;)),
          drawer:
              Drawer(child: SafeArea(child: ListView(children: &amp;lt;Widget&amp;gt;[]))),
          body: ListView.builder(
            itemCount: 7,
            scrollDirection: Axis.horizontal,
            itemBuilder: (BuildContext context, int index) {
              final int i = index * 12;
              return SafeArea(
                child: Stack(children: &amp;lt;Widget&amp;gt;[
                  Row(mainAxisSize: MainAxisSize.min, children: &amp;lt;Widget&amp;gt;[
                    _buildKey(24 + i, false),
                    _buildKey(26 + i, false),
                    _buildKey(28 + i, false),
                    _buildKey(29 + i, false),
                    _buildKey(31 + i, false),
                    _buildKey(33 + i, false),
                    _buildKey(35 + i, false),
                  ]),
                  Positioned(
                      left: 0.0,
                      right: 0.0,
                      bottom: 100,
                      top: 0.0,
                      child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          mainAxisSize: MainAxisSize.min,
                          children: &amp;lt;Widget&amp;gt;[
                            Container(width: keyWidth * .5),
                            _buildKey(25 + i, true),
                            _buildKey(27 + i, true),
                            Container(width: keyWidth),
                            _buildKey(30 + i, true),
                            _buildKey(32 + i, true),
                            _buildKey(34 + i, true),
                            Container(width: keyWidth * .5),
                          ])),
                ]),
              );
            },
          )),
    );
  }

  Widget _buildKey(int midi, bool accidental) {
    if (accidental) {
      return Container(
          width: keyWidth,
          color: Colors.black,
          margin: EdgeInsets.symmetric(horizontal: 2.0),
          padding: EdgeInsets.symmetric(horizontal: keyWidth * .1),
          child: Material(
            elevation: 6.0,
            borderRadius: borderRadius,
            shadowColor: Color(0x802196F3),
          ));
    }
    return Container(
        width: keyWidth,
        color: Colors.white,
        margin: EdgeInsets.symmetric(horizontal: 2.0));
  }
}

const BorderRadiusGeometry borderRadius = BorderRadius.only(
    bottomLeft: Radius.circular(10.0), bottomRight: Radius.circular(10.0));

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 9&lt;/h2&gt;
&lt;p&gt;Time to add midi by adding the following import to the top of the file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:tonic/tonic.dart&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;-buildKey&lt;/code&gt; function you can add this line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; final pitchName = Pitch.fromMidiNumber(midi).toString();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also create the piano key itself underneath it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; final pianoKey = Stack(
     children: &amp;lt;Widget&amp;gt;[
       Semantics(
           button: true,
           hint: pitchName,
           child: Material(
               borderRadius: borderRadius,
               color: accidental ? Colors.black : Colors.white,
               child: InkWell(
                 borderRadius: borderRadius,
                 highlightColor: Colors.grey,
                 onTap: () {},
                 onTapDown: (_) =&amp;gt; FlutterMidi.playMidiNote(midi: midi),
               ))),
       Positioned(
           left: 0.0,
           right: 0.0,
           bottom: 20.0,
           child: _showLabels
               ? Text(pitchName,
                   textAlign: TextAlign.center,
                   style: TextStyle(
                       color: !accidental ? Colors.black : Colors.white))
               : Container()),
     ],
   );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remove the color from the container and replace it with &lt;code&gt;child: pianoKey,&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; if (accidental) {
     return Container(
         width: keyWidth,
         margin: EdgeInsets.symmetric(horizontal: 2.0),
         padding: EdgeInsets.symmetric(horizontal: keyWidth * .1),
         child: Material(
             elevation: 6.0,
             borderRadius: borderRadius,
             shadowColor: Color(0x802196F3),
             child: pianoKey));
   }
   return Container(
       width: keyWidth,
       child: pianoKey,
       margin: EdgeInsets.symmetric(horizontal: 2.0));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The complete function should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; Widget _buildKey(int midi, bool accidental) {
   final pitchName = Pitch.fromMidiNumber(midi).toString();
   final pianoKey = Stack(
     children: &amp;lt;Widget&amp;gt;[
       Semantics(
           button: true,
           hint: pitchName,
           child: Material(
               borderRadius: borderRadius,
               color: accidental ? Colors.black : Colors.white,
               child: InkWell(
                 borderRadius: borderRadius,
                 highlightColor: Colors.grey,
                 onTap: () {},
                 onTapDown: (_) =&amp;gt; FlutterMidi.playMidiNote(midi: midi),
               ))),
       Positioned(
           left: 0.0,
           right: 0.0,
           bottom: 20.0,
           child: _showLabels
               ? Text(pitchName,
                   textAlign: TextAlign.center,
                   style: TextStyle(
                       color: !accidental ? Colors.black : Colors.white))
               : Container()),
     ],
   );
   if (accidental) {
     return Container(
         width: keyWidth,
         margin: EdgeInsets.symmetric(horizontal: 2.0),
         padding: EdgeInsets.symmetric(horizontal: keyWidth * .1),
         child: Material(
             elevation: 6.0,
             borderRadius: borderRadius,
             shadowColor: Color(0x802196F3),
             child: pianoKey));
   }
   return Container(
       width: keyWidth,
       child: pianoKey,
       margin: EdgeInsets.symmetric(horizontal: 2.0));
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you run the app it should look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_9_bvdvxhujh2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Almost there! Now let&apos;s give our user some control.&lt;/p&gt;
&lt;h2&gt;Step 10&lt;/h2&gt;
&lt;p&gt;Add these settings to the &lt;code&gt;Drawer&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;Container(height: 20.0),
ListTile(title: Text(&amp;quot;Change Width&amp;quot;)),
Slider(
    activeColor: Colors.redAccent,
    inactiveColor: Colors.white,
    min: 0.0,
    max: 1.0,
    value: _widthRatio,
    onChanged: (double value) =&amp;gt;
        setState(() =&amp;gt; _widthRatio = value)),
Divider(),
ListTile(
    title: Text(&amp;quot;Show Labels&amp;quot;),
    trailing: Switch(
    value: _showLabels,
    onChanged: (bool value) =&amp;gt;
        setState(() =&amp;gt; _showLabels = value))),
Divider(),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you should see this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_10_6ib9jspceh.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Step 11&lt;/h2&gt;
&lt;p&gt;To start with &lt;code&gt;Middle C&lt;/code&gt; lets add an inital scroll offset to the &lt;code&gt;ListView&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;controller: ScrollController(initialScrollOffset: 1500.0),&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now when we start the app it should co to C4.&lt;/p&gt;
&lt;p&gt;The final App should look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/piano_11_w2jua2mews.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The final code should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter/services.dart&apos;;
import &apos;package:flutter_midi/flutter_midi.dart&apos;;
import &apos;package:tonic/tonic.dart&apos;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  @override
  initState() {
    FlutterMidi.unmute();
    rootBundle.load(&amp;quot;assets/sounds/Piano.sf2&amp;quot;).then((sf2) {
      FlutterMidi.prepare(sf2: sf2, name: &amp;quot;Piano.sf2&amp;quot;);
    });
    super.initState();
  }

  double get keyWidth =&amp;gt; 80 + (80 * _widthRatio);
  double _widthRatio = 0.0;
  bool _showLabels = true;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData.dark(),
      home: Scaffold(
          appBar: AppBar(title: Text(&amp;quot;Flutter Piano&amp;quot;)),
          drawer: Drawer(
              child: SafeArea(
                  child: ListView(children: &amp;lt;Widget&amp;gt;[
            Container(height: 20.0),
            ListTile(title: Text(&amp;quot;Change Width&amp;quot;)),
            Slider(
                activeColor: Colors.redAccent,
                inactiveColor: Colors.white,
                min: 0.0,
                max: 1.0,
                value: _widthRatio,
                onChanged: (double value) =&amp;gt;
                    setState(() =&amp;gt; _widthRatio = value)),
            Divider(),
            ListTile(
                title: Text(&amp;quot;Show Labels&amp;quot;),
                trailing: Switch(
                    value: _showLabels,
                    onChanged: (bool value) =&amp;gt;
                        setState(() =&amp;gt; _showLabels = value))),
            Divider(),
          ]))),
          body: ListView.builder(
            itemCount: 7,
            scrollDirection: Axis.horizontal,
            controller: ScrollController(initialScrollOffset: 1500.0),
            itemBuilder: (BuildContext context, int index) {
              final int i = index * 12;
              return SafeArea(
                child: Stack(children: &amp;lt;Widget&amp;gt;[
                  Row(mainAxisSize: MainAxisSize.min, children: &amp;lt;Widget&amp;gt;[
                    _buildKey(24 + i, false),
                    _buildKey(26 + i, false),
                    _buildKey(28 + i, false),
                    _buildKey(29 + i, false),
                    _buildKey(31 + i, false),
                    _buildKey(33 + i, false),
                    _buildKey(35 + i, false),
                  ]),
                  Positioned(
                      left: 0.0,
                      right: 0.0,
                      bottom: 100,
                      top: 0.0,
                      child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          mainAxisSize: MainAxisSize.min,
                          children: &amp;lt;Widget&amp;gt;[
                            Container(width: keyWidth * .5),
                            _buildKey(25 + i, true),
                            _buildKey(27 + i, true),
                            Container(width: keyWidth),
                            _buildKey(30 + i, true),
                            _buildKey(32 + i, true),
                            _buildKey(34 + i, true),
                            Container(width: keyWidth * .5),
                          ])),
                ]),
              );
            },
          )),
    );
  }

  Widget _buildKey(int midi, bool accidental) {
    final pitchName = Pitch.fromMidiNumber(midi).toString();
    final pianoKey = Stack(
      children: &amp;lt;Widget&amp;gt;[
        Semantics(
            button: true,
            hint: pitchName,
            child: Material(
                borderRadius: borderRadius,
                color: accidental ? Colors.black : Colors.white,
                child: InkWell(
                  borderRadius: borderRadius,
                  highlightColor: Colors.grey,
                  onTap: () {},
                  onTapDown: (_) =&amp;gt; FlutterMidi.playMidiNote(midi: midi),
                ))),
        Positioned(
            left: 0.0,
            right: 0.0,
            bottom: 20.0,
            child: _showLabels
                ? Text(pitchName,
                    textAlign: TextAlign.center,
                    style: TextStyle(
                        color: !accidental ? Colors.black : Colors.white))
                : Container()),
      ],
    );
    if (accidental) {
      return Container(
          width: keyWidth,
          margin: EdgeInsets.symmetric(horizontal: 2.0),
          padding: EdgeInsets.symmetric(horizontal: keyWidth * .1),
          child: Material(
              elevation: 6.0,
              borderRadius: borderRadius,
              shadowColor: Color(0x802196F3),
              child: pianoKey));
    }
    return Container(
        width: keyWidth,
        child: pianoKey,
        margin: EdgeInsets.symmetric(horizontal: 2.0));
  }
}

const BorderRadiusGeometry borderRadius = BorderRadius.only(
    bottomLeft: Radius.circular(10.0), bottomRight: Radius.circular(10.0));

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;If you delete &lt;code&gt;tests/&lt;/code&gt; and run &lt;code&gt;find . -name &amp;quot;*.dart&amp;quot; | xargs cat | wc -c&lt;/code&gt; you will see that the dart code only uses &lt;code&gt;5032&lt;/code&gt; bytes of space!&lt;/p&gt;
&lt;p&gt;Now we have a fully functional piano that you can play with and enjoy on iOS and Android.&lt;/p&gt;
&lt;p&gt;I was really inspired when creating this for the &lt;a href=&quot;https://flutter.dev/create&quot;&gt;Flutter Create&lt;/a&gt; contest.&lt;/p&gt;
&lt;p&gt;Hope you learned something, if you have any questions you can always read out to me. This is an open source piano and would love PRs on the main project &lt;a href=&quot;https://github.com/rodydavis/flutter_piano&quot;&gt;here&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
</content:encoded></item><item><title>JSON to HTML Table with Lit</title><link>https://rodydavis.com/lit/html-table/</link><guid isPermaLink="true">https://rodydavis.com/lit/html-table/</guid><description>Learn how to create dynamic HTML tables using a Lit web component that can generate tables either from a remote JSON URL or inline JSON data.</description><pubDate>Mon, 20 Jan 2025 01:18:07 GMT</pubDate><content:encoded>&lt;h1&gt;JSON to HTML Table with Lit&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to create a HTML &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table&quot;&gt;Table&lt;/a&gt; from json url or inline json.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/lit-html-table&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/lit-html-table/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;lit-html-table&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd lit-html-table
npm i lit
npm i -D @types/node
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &amp;quot;/lit-html-table/&amp;quot;,
  build: {
    lib: {
      entry: &amp;quot;src/lit-html-table.ts&amp;quot;,
      formats: [&amp;quot;es&amp;quot;],
    },
    rollupOptions: {
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;JSON to Lit HTML Table&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/lit-html-table.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;lit-html-table src=&amp;quot;https://jsonplaceholder.typicode.com/posts&amp;quot;&amp;gt;
      &amp;lt;!-- &amp;lt;span slot=&amp;quot;title&amp;quot; style=&amp;quot;color: red;&amp;quot;&amp;gt;Title&amp;lt;/span&amp;gt; --&amp;gt;
      &amp;lt;!-- &amp;lt;script type=&amp;quot;application/json&amp;quot;&amp;gt;
      [
        {
          &amp;quot;id&amp;quot;: &amp;quot;0&amp;quot;,
          &amp;quot;name&amp;quot;: &amp;quot;First Item&amp;quot;
        }
      ]
    &amp;lt;/script&amp;gt; --&amp;gt;
    &amp;lt;/lit-html-table&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are passing a src attribute to the web component for this example but we can also add a script tag with the type attribute set to &lt;code&gt;application/json&lt;/code&gt; with the contents containing the json.&lt;/p&gt;
&lt;p&gt;If any table header cell needed to be replaced an element can be provided with the slot name set to the key in the json object.&lt;/p&gt;
&lt;h2&gt;Web Component&lt;/h2&gt;
&lt;p&gt;Before we update our component we need to rename &lt;code&gt;my-element.ts&lt;/code&gt; to &lt;code&gt;lit-html-table.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Open up &lt;code&gt;lit-html-table.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;

type ObjectData = { [key: string]: any };

@customElement(&amp;quot;lit-html-table&amp;quot;)
export class LitHtmlTable extends LitElement {
  @property() src = &amp;quot;&amp;quot;;

  data?: ObjectData[];

  static styles = css`
    tr {
      text-align: var(--table-tr-text-align, left);
      vertical-align: var(--table-tr-vertical-align, top);
      padding: var(--table-tr-padding, 10px);
    }
  `;

  render() {
    // Check if data is loaded
    if (!this.values) {
      return html`&amp;lt;slot name=&amp;quot;loading&amp;quot;&amp;gt;Loading...&amp;lt;/slot&amp;gt;`;
    }
    // Check if items are not empty
    if (this.values.length === 0) {
      return html`&amp;lt;slot name=&amp;quot;empty&amp;quot;&amp;gt;No Items Found!&amp;lt;/slot&amp;gt;`;
    }
    // Convert JSON to HTML Table
    return html`
      &amp;lt;table&amp;gt;
        &amp;lt;thead&amp;gt;
          &amp;lt;tr&amp;gt;
            ${Object.keys(this.values[0]).map((key) =&amp;gt; {
              const name = key.replace(/\b([a-z])/g, (_, val) =&amp;gt;
                val.toUpperCase()
              );
              return html`&amp;lt;th&amp;gt;
                &amp;lt;slot name=&amp;quot;${key}&amp;quot;&amp;gt;${name}&amp;lt;/slot&amp;gt;
              &amp;lt;/th&amp;gt;`;
            })}
          &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;
          ${this.values.map((item) =&amp;gt; {
            return html`
              &amp;lt;tr&amp;gt;
                ${Object.values(item).map((row) =&amp;gt; {
                  return html`&amp;lt;td&amp;gt;${row}&amp;lt;/td&amp;gt;`;
                })}
              &amp;lt;/tr&amp;gt;
            `;
          })}
        &amp;lt;/tbody&amp;gt;
      &amp;lt;/table&amp;gt;
    `;
  }

  async firstUpdated() {
    await this.fetchData();
  }

  // Download the latest json and update it locally
  async fetchData() {
    let _data: any;
    if (this.src.length &amp;gt; 0) {
      // If a src attribute is set prefer it over any slots
      _data = await fetch(this.src).then((res) =&amp;gt; res.json());
    } else {
      // If no src attribute is set then grab the inline json in the slot
      const elem = this.parentElement?.querySelector(
        &apos;script[type=&amp;quot;application/json&amp;quot;]&apos;
      ) as HTMLScriptElement;
      if (elem) _data = JSON.parse(elem.innerHTML);
    }
    this.values = this.transform(_data ?? []);
    this.requestUpdate();
  }

  transform(data: any) {
    return data;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have defined a few &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/--*&quot;&gt;CSS Custom Properties&lt;/a&gt; to style the table cell but many more can be added here.&lt;/p&gt;
&lt;p&gt;If everything goes well run the command &lt;code&gt;npm run dev&lt;/code&gt; and the follow should appear:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/h_1_dab8xdgmxb.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Editing&lt;/h2&gt;
&lt;p&gt;What if we wanted to support editing of any cell? With Lit and Web Components we can progressively enhance the experience without changing the html.&lt;/p&gt;
&lt;p&gt;At the top of the class add the following boolean property:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;@property({ type: Boolean }) editable = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now update the &lt;code&gt;tbody&lt;/code&gt; tag in the render method:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;&amp;lt;tbody&amp;gt;
  ${this.values.map((item, index) =&amp;gt; {
    return html`
      &amp;lt;tr&amp;gt;
        ${Object.entries(item).map((row) =&amp;gt; {
          return html`&amp;lt;td&amp;gt;
            ${this.editable
              ? html`&amp;lt;input
                  value=&amp;quot;${row[1]}&amp;quot;
                  type=&amp;quot;text&amp;quot;
                  @input=${(e: any) =&amp;gt; {
                    const value = e.target.value;
                    const key = row[0];
                    const current = this.values![index];
                    current[key] = value;
                    this.values![index] = current;
                    this.requestUpdate();
                    this.dispatchEvent(
                      new CustomEvent(&amp;quot;input-cell&amp;quot;, {
                        detail: {
                          index: index,
                          data: current,
                        },
                      })
                    );
                  }}
                /&amp;gt;`
              : html`${row[1]}`}
          &amp;lt;/td&amp;gt;`;
        })}
      &amp;lt;/tr&amp;gt;
    `;
  })}
&amp;lt;/tbody&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By checking to see if the &lt;code&gt;editable&lt;/code&gt; and if &lt;code&gt;true&lt;/code&gt; return an input with an event listener to update the data and dispatch an &lt;code&gt;input&lt;/code&gt; event.&lt;/p&gt;
&lt;p&gt;Add the &lt;code&gt;editable&lt;/code&gt; attribute to the &lt;code&gt;index.html&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;lit-html-table editable&amp;gt; ... &amp;lt;/lit-html-table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a reload the table should look like this and any cell can be edited.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/h_2_8s8pheteqq.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;An event listener can be added just before the closing &lt;code&gt;body&lt;/code&gt; tag in &lt;code&gt;index.html&lt;/code&gt; to grab the latest values or cell information:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;script&amp;gt;
  const elem = document.querySelector(&amp;quot;lit-html-table&amp;quot;);
  elem.addEventListener(
    &amp;quot;input-cell&amp;quot;,
    (e) =&amp;gt; {
      // Index and data for the individual cell
      const { index, data } = e.detail;
      // New array of json items
      const values = elem.values;
    },
    false
  );
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be taken farther by checking for the type of the value and returning a color, number or checkbox input.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building with Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;. There is also an example on the Lit playground &lt;a href=&quot;https://lit.dev/playground/#project=W3sibmFtZSI6ImxpdC1odG1sLXRhYmxlLnRzIiwiY29udGVudCI6ImltcG9ydCB7IGh0bWwsIGNzcywgTGl0RWxlbWVudCB9IGZyb20gXCJsaXRcIjtcbmltcG9ydCB7IGN1c3RvbUVsZW1lbnQsIHByb3BlcnR5IH0gZnJvbSBcImxpdC9kZWNvcmF0b3JzLmpzXCI7XG5cbnR5cGUgT2JqZWN0RGF0YSA9IHsgW2tleTogc3RyaW5nXTogYW55IH07XG5cbkBjdXN0b21FbGVtZW50KFwibGl0LWh0bWwtdGFibGVcIilcbmV4cG9ydCBjbGFzcyBMaXRIdG1sVGFibGUgZXh0ZW5kcyBMaXRFbGVtZW50IHtcbiAgQHByb3BlcnR5KCkgc3JjID0gXCJcIjtcblxuICBkYXRhPzogT2JqZWN0RGF0YVtdO1xuXG4gIHN0YXRpYyBzdHlsZXMgPSBjc3NgXG4gICAgdHIge1xuICAgICAgdGV4dC1hbGlnbjogdmFyKC0tdGFibGUtdHItdGV4dC1hbGlnbiwgbGVmdCk7XG4gICAgICB2ZXJ0aWNhbC1hbGlnbjogdmFyKC0tdGFibGUtdHItdmVydGljYWwtYWxpZ24sIHRvcCk7XG4gICAgICBwYWRkaW5nOiB2YXIoLS10YWJsZS10ci1wYWRkaW5nLCAxMHB4KTtcbiAgICB9XG4gIGA7XG5cbiAgcmVuZGVyKCkge1xuICAgIC8vIENoZWNrIGlmIGRhdGEgaXMgbG9hZGVkXG4gICAgaWYgKCF0aGlzLmRhdGEpIHtcbiAgICAgIHJldHVybiBodG1sYDxzbG90IG5hbWU9XCJsb2FkaW5nXCI-TG9hZGluZy4uLjwvc2xvdD5gO1xuICAgIH1cbiAgICAvLyBDaGVjayBpZiBpdGVtcyBhcmUgbm90IGVtcHR5XG4gICAgaWYgKHRoaXMuZGF0YS5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBodG1sYDxzbG90IG5hbWU9XCJlbXB0eVwiPk5vIEl0ZW1zIEZvdW5kITwvc2xvdD5gO1xuICAgIH1cbiAgICAvLyBDb252ZXJ0IEpTT04gdG8gSFRNTCBUYWJsZVxuICAgIHJldHVybiBodG1sYFxuICAgICAgPHRhYmxlPlxuICAgICAgICA8dGhlYWQ-XG4gICAgICAgICAgPHRyPlxuICAgICAgICAgICAgJHtPYmplY3Qua2V5cyh0aGlzLmRhdGFbMF0pLm1hcCgoa2V5KSA9PiB7XG4gICAgICAgICAgICAgIGNvbnN0IG5hbWUgPSBrZXkucmVwbGFjZSgvXFxiKFthLXpdKS9nLCAoXywgdmFsKSA9PlxuICAgICAgICAgICAgICAgIHZhbC50b1VwcGVyQ2FzZSgpXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIHJldHVybiBodG1sYDx0aD5cbiAgICAgICAgICAgICAgICA8c2xvdCBuYW1lPVwiJHtrZXl9XCI-JHtuYW1lfTwvc2xvdD5cbiAgICAgICAgICAgICAgPC90aD5gO1xuICAgICAgICAgICAgfSl9XG4gICAgICAgICAgPC90cj5cbiAgICAgICAgPC90aGVhZD5cbiAgICAgICAgPHRib2R5PlxuICAgICAgICAgICR7dGhpcy5kYXRhLm1hcCgoaXRlbSkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIGh0bWxgXG4gICAgICAgICAgICAgIDx0cj5cbiAgICAgICAgICAgICAgICAke09iamVjdC52YWx1ZXMoaXRlbSkubWFwKCh2YWwpID0-IHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBodG1sYDx0ZD4ke3ZhbH08L3RkPmA7XG4gICAgICAgICAgICAgICAgfSl9XG4gICAgICAgICAgICAgIDwvdHI-XG4gICAgICAgICAgICBgO1xuICAgICAgICAgIH0pfVxuICAgICAgICA8L3Rib2R5PlxuICAgICAgPC90YWJsZT5cbiAgICBgO1xuICB9XG5cbiAgYXN5bmMgZmlyc3RVcGRhdGVkKCkge1xuICAgIGF3YWl0IHRoaXMuZmV0Y2hEYXRhKCk7XG4gIH1cblxuICBhc3luYyBmZXRjaERhdGEoKSB7XG4gICAgbGV0IF9kYXRhOiBhbnk7XG4gICAgaWYgKHRoaXMuc3JjLmxlbmd0aCA-IDApIHtcbiAgICAgIF9kYXRhID0gYXdhaXQgZmV0Y2godGhpcy5zcmMpLnRoZW4oKHJlcykgPT4gcmVzLmpzb24oKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGVsZW0gPSB0aGlzLnBhcmVudEVsZW1lbnQ_LnF1ZXJ5U2VsZWN0b3IoXG4gICAgICAgICdzY3JpcHRbdHlwZT1cImFwcGxpY2F0aW9uL2pzb25cIl0nXG4gICAgICApIGFzIEhUTUxTY3JpcHRFbGVtZW50O1xuICAgICAgaWYgKGVsZW0pIF9kYXRhID0gSlNPTi5wYXJzZShlbGVtLmlubmVySFRNTCk7XG4gICAgfVxuICAgIF9kYXRhID8_PSBbXTtcbiAgICB0aGlzLmRhdGEgPSB0aGlzLnRyYW5zZm9ybShfZGF0YSk7XG4gICAgdGhpcy5yZXF1ZXN0VXBkYXRlKCk7XG4gIH1cblxuICB0cmFuc2Zvcm0oZGF0YTogYW55KSB7XG4gICAgcmV0dXJuIGRhdGE7XG4gIH1cbn1cbiJ9LHsibmFtZSI6ImluZGV4Lmh0bWwiLCJjb250ZW50IjoiPCFET0NUWVBFIGh0bWw-XG48aHRtbCBsYW5nPVwiZW5cIj5cblxuPGhlYWQ-XG4gIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiIC8-XG4gIDxsaW5rIHJlbD1cImljb25cIiB0eXBlPVwiaW1hZ2Uvc3ZnK3htbFwiIGhyZWY9XCIvc3JjL2Zhdmljb24uc3ZnXCIgLz5cbiAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIiAvPlxuICA8dGl0bGU-SlNPTiB0byBMaXQgSFRNTCBUYWJsZTwvdGl0bGU-XG4gIDxzY3JpcHQgdHlwZT1cIm1vZHVsZVwiIHNyYz1cIi4vbGl0LWh0bWwtdGFibGUuanNcIj48L3NjcmlwdD5cbjwvaGVhZD5cblxuPGJvZHk-XG4gIDxsaXQtaHRtbC10YWJsZSBzcmM9XCJodHRwczovL2pzb25wbGFjZWhvbGRlci50eXBpY29kZS5jb20vcG9zdHNcIj5cbiAgICA8IS0tIDxzcGFuIHNsb3Q9XCJ0aXRsZVwiIHN0eWxlPVwiY29sb3I6IHJlZDtcIj5UaXRsZTwvc3Bhbj4gLS0-XG4gICAgPCEtLSA8c2NyaXB0IHR5cGU9XCJhcHBsaWNhdGlvbi9qc29uXCI-XG4gICAgICBbXG4gICAgICAgIHtcbiAgICAgICAgICBcImlkXCI6IFwiMFwiLFxuICAgICAgICAgIFwibmFtZVwiOiBcIkZpcnN0IEl0ZW1cIlxuICAgICAgICB9XG4gICAgICBdXG4gICAgPC9zY3JpcHQ-IC0tPlxuICA8L2xpdC1odG1sLXRhYmxlPlxuXG48L2JvZHk-XG5cbjwvaHRtbD4ifV0&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source for this example can be found &lt;a href=&quot;https://github.com/rodydavis/lit-html-table&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Lit and Monaco Editor</title><link>https://rodydavis.com/lit/monaco-editor/</link><guid isPermaLink="true">https://rodydavis.com/lit/monaco-editor/</guid><description>This guide provides a technical tutorial on how to integrate the powerful Monaco Editor (the engine behind VSCode) into a reusable web component using Lit.</description><pubDate>Mon, 20 Jan 2025 02:32:31 GMT</pubDate><content:encoded>&lt;h1&gt;Lit and Monaco Editor&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to wrap the &lt;a href=&quot;https://microsoft.github.io/monaco-editor/&quot;&gt;Monaco Editor&lt;/a&gt; that powers &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VSCode&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; You can find the final source &lt;a href=&quot;https://github.com/rodydavis/lit-code-editor&quot;&gt;here&lt;/a&gt; and an online demo &lt;a href=&quot;https://rodydavis.github.io/lit-code-editor/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To learn how to build an extension with VSCode and Lit check out the blog post &lt;a href=&quot;https://rodydavis.com/posts/lit-vscode-extension/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;lit-code-editor&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd lit-code-editor
npm i lit monaco-editor
npm i -D @types/node
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &amp;quot;/lit-code-editor/&amp;quot;,
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Lit Code Editor&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/code-editor.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100vh;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;code-editor&amp;gt;
      &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
function x() {
  console.log(&amp;quot;Hello world! :)&amp;quot;);
}
      &amp;lt;/script&amp;gt;
    &amp;lt;/code-editor&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are setting up the &lt;code&gt;lit-element&lt;/code&gt; to have a slot which will be the code for the editor to start with. The language can be set with the type or adding an attribute to the &lt;code&gt;code-editor&lt;/code&gt; component.&lt;/p&gt;
&lt;h2&gt;Web Component&lt;/h2&gt;
&lt;p&gt;Before we update our component we need to rename &lt;code&gt;my-element.ts&lt;/code&gt; to &lt;code&gt;code-editor.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Open up &lt;code&gt;code-editor.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { css, html, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;
import { createRef, Ref, ref } from &amp;quot;lit/directives/ref.js&amp;quot;;

// -- Monaco Editor Imports --
import * as monaco from &amp;quot;monaco-editor&amp;quot;;
import styles from &amp;quot;monaco-editor/min/vs/editor/editor.main.css&amp;quot;;
import editorWorker from &amp;quot;monaco-editor/esm/vs/editor/editor.worker?worker&amp;quot;;
import jsonWorker from &amp;quot;monaco-editor/esm/vs/language/json/json.worker?worker&amp;quot;;
import cssWorker from &amp;quot;monaco-editor/esm/vs/language/css/css.worker?worker&amp;quot;;
import htmlWorker from &amp;quot;monaco-editor/esm/vs/language/html/html.worker?worker&amp;quot;;
import tsWorker from &amp;quot;monaco-editor/esm/vs/language/typescript/ts.worker?worker&amp;quot;;

// @ts-ignore
self.MonacoEnvironment = {
  getWorker(_: any, label: string) {
    if (label === &amp;quot;json&amp;quot;) {
      return new jsonWorker();
    }
    if (label === &amp;quot;css&amp;quot; || label === &amp;quot;scss&amp;quot; || label === &amp;quot;less&amp;quot;) {
      return new cssWorker();
    }
    if (label === &amp;quot;html&amp;quot; || label === &amp;quot;handlebars&amp;quot; || label === &amp;quot;razor&amp;quot;) {
      return new htmlWorker();
    }
    if (label === &amp;quot;typescript&amp;quot; || label === &amp;quot;javascript&amp;quot;) {
      return new tsWorker();
    }
    return new editorWorker();
  },
};

@customElement(&amp;quot;code-editor&amp;quot;)
export class CodeEditor extends LitElement {
  private container: Ref&amp;lt;HTMLElement&amp;gt; = createRef();
  editor?: monaco.editor.IStandaloneCodeEditor;
  @property() theme?: string;
  @property() language?: string;
  @property() code?: string;

  static styles = css`
    :host {
      --editor-width: 100%;
      --editor-height: 100vh;
    }
    main {
      width: var(--editor-width);
      height: var(--editor-height);
    }
  `;

  render() {
    return html`
      &amp;lt;style&amp;gt;
        ${styles}
      &amp;lt;/style&amp;gt;
      &amp;lt;main ${ref(this.container)}&amp;gt;&amp;lt;/main&amp;gt;
    `;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    &amp;quot;code-editor&amp;quot;: CodeEditor;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are just setting up some boilerplate to set up the &lt;a href=&quot;https://vitejs.dev/guide/features.html#web-workers&quot;&gt;web workers with vite&lt;/a&gt; and passing the reference from the container element to the template using the &lt;a href=&quot;https://lit.dev/docs/templates/directives/#ref&quot;&gt;ref directive&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The styles from monaco editor are also passed as a style element load in the shadow root.&lt;/p&gt;
&lt;p&gt;Now let&apos;s add some helper methods for accessing the code and language provided:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;private getFile() {
  if (this.children.length &amp;gt; 0) return this.children[0];
  return null;
}

private getCode() {
  if (this.code) return this.code;
  const file = this.getFile();
  if (!file) return;
  return file.innerHTML.trim();
}

private getLang() {
  if (this.language) return this.language;
  const file = this.getFile();
  if (!file) return;
  const type = file.getAttribute(&amp;quot;type&amp;quot;)!;
  return type.split(&amp;quot;/&amp;quot;).pop()!;
}

private getTheme() {
  if (this.theme) return this.theme;
  if (this.isDark()) return &amp;quot;vs-dark&amp;quot;;
  return &amp;quot;vs-light&amp;quot;;
}

private isDark() {
  return (
    window.matchMedia &amp;amp;&amp;amp;
    window.matchMedia(&amp;quot;(prefers-color-scheme: dark)&amp;quot;).matches
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These methods are checking the slot for the script tag with the language provided or looking for a property set on &lt;code&gt;code-editor&lt;/code&gt; and then returning the value.&lt;/p&gt;
&lt;p&gt;Now let&apos;s attach the editor to the container reference:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;firstUpdated() {
  this.editor = monaco.editor.create(this.container.value!, {
    value: this.getCode(),
    language: this.getLang(),
    theme: this.getTheme(),
    automaticLayout: true,
  });
   window
    .matchMedia(&amp;quot;(prefers-color-scheme: dark)&amp;quot;)
    .addEventListener(&amp;quot;change&amp;quot;, () =&amp;gt; {
      monaco.editor.setTheme(this.getTheme());
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the editor should be running and able to be interacted with:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/m_1_tsfmich8kz.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;When the system changes to dark mode it will &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme&quot;&gt;switch&lt;/a&gt; as well!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/m_2_ggpw2nyy0d.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;To get and set the value from the editor we can add 2 helper methods:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;setValue(value: string) {
  this.editor!.setValue(value);
}

getValue() {
  const value = this.editor!.getValue();
  return value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything should work as expected now and the final code should look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { css, html, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;
import { createRef, Ref, ref } from &amp;quot;lit/directives/ref.js&amp;quot;;

// -- Monaco Editor Imports --
import * as monaco from &amp;quot;monaco-editor&amp;quot;;
import styles from &amp;quot;monaco-editor/min/vs/editor/editor.main.css&amp;quot;;
import editorWorker from &amp;quot;monaco-editor/esm/vs/editor/editor.worker?worker&amp;quot;;
import jsonWorker from &amp;quot;monaco-editor/esm/vs/language/json/json.worker?worker&amp;quot;;
import cssWorker from &amp;quot;monaco-editor/esm/vs/language/css/css.worker?worker&amp;quot;;
import htmlWorker from &amp;quot;monaco-editor/esm/vs/language/html/html.worker?worker&amp;quot;;
import tsWorker from &amp;quot;monaco-editor/esm/vs/language/typescript/ts.worker?worker&amp;quot;;

// @ts-ignore
self.MonacoEnvironment = {
  getWorker(_: any, label: string) {
    if (label === &amp;quot;json&amp;quot;) {
      return new jsonWorker();
    }
    if (label === &amp;quot;css&amp;quot; || label === &amp;quot;scss&amp;quot; || label === &amp;quot;less&amp;quot;) {
      return new cssWorker();
    }
    if (label === &amp;quot;html&amp;quot; || label === &amp;quot;handlebars&amp;quot; || label === &amp;quot;razor&amp;quot;) {
      return new htmlWorker();
    }
    if (label === &amp;quot;typescript&amp;quot; || label === &amp;quot;javascript&amp;quot;) {
      return new tsWorker();
    }
    return new editorWorker();
  },
};

@customElement(&amp;quot;code-editor&amp;quot;)
export class CodeEditor extends LitElement {
  private container: Ref&amp;lt;HTMLElement&amp;gt; = createRef();
  editor?: monaco.editor.IStandaloneCodeEditor;
  @property() theme?: string;
  @property() language?: string;
  @property() code?: string;

  static styles = css`
    :host {
      --editor-width: 100%;
      --editor-height: 100vh;
    }
    main {
      width: var(--editor-width);
      height: var(--editor-height);
    }
  `;

  render() {
    return html`
      &amp;lt;style&amp;gt;
        ${styles}
      &amp;lt;/style&amp;gt;
      &amp;lt;main ${ref(this.container)}&amp;gt;&amp;lt;/main&amp;gt;
    `;
  }

  private getFile() {
    if (this.children.length &amp;gt; 0) return this.children[0];
    return null;
  }

  private getCode() {
    if (this.code) return this.code;
    const file = this.getFile();
    if (!file) return;
    return file.innerHTML.trim();
  }

  private getLang() {
    if (this.language) return this.language;
    const file = this.getFile();
    if (!file) return;
    const type = file.getAttribute(&amp;quot;type&amp;quot;)!;
    return type.split(&amp;quot;/&amp;quot;).pop()!;
  }

  private getTheme() {
    if (this.theme) return this.theme;
    if (this.isDark()) return &amp;quot;vs-dark&amp;quot;;
    return &amp;quot;vs-light&amp;quot;;
  }

  private isDark() {
    return (
      window.matchMedia &amp;amp;&amp;amp;
      window.matchMedia(&amp;quot;(prefers-color-scheme: dark)&amp;quot;).matches
    );
  }

  setValue(value: string) {
    this.editor!.setValue(value);
  }

  getValue() {
    const value = this.editor!.getValue();
    return value;
  }

  firstUpdated() {
    this.editor = monaco.editor.create(this.container.value!, {
      value: this.getCode(),
      language: this.getLang(),
      theme: this.getTheme(),
      automaticLayout: true,
    });
    window
      .matchMedia(&amp;quot;(prefers-color-scheme: dark)&amp;quot;)
      .addEventListener(&amp;quot;change&amp;quot;, () =&amp;gt; {
        monaco.editor.setTheme(this.getTheme());
      });
  }
}

declare global {
  interface HTMLElementTagNameMap {
    &amp;quot;code-editor&amp;quot;: CodeEditor;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Usage&lt;/h2&gt;
&lt;p&gt;To use this component it can have the code provided by slots:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;code-editor&amp;gt;
  &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
function x() {
  console.log(&amp;quot;Hello world! :)&amp;quot;);
}
  &amp;lt;/script&amp;gt;
&amp;lt;/code-editor&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or for properties:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;code-editor 
  code=&amp;quot;console.log(&apos;Hello World&apos;);&amp;quot; 
  language=&amp;quot;javascript&amp;quot;
  &amp;gt;
&amp;lt;/code-editor&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or both:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;code-editor language=&amp;quot;typescript&amp;quot;&amp;gt;
  &amp;lt;script&amp;gt;
function x() {
  console.log(&amp;quot;Hello world! :)&amp;quot;);
}
  &amp;lt;/script&amp;gt;
&amp;lt;/code-editor&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The theme can also be manually set:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;code-editor theme=&amp;quot;vs-light&amp;quot;&amp;gt; &amp;lt;/code-editor&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building with Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source for this example can be found &lt;a href=&quot;https://github.com/rodydavis/lit-code-editor&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Building a Rich Text Editor with Lit</title><link>https://rodydavis.com/lit/rich-text-editor/</link><guid isPermaLink="true">https://rodydavis.com/lit/rich-text-editor/</guid><description>How to build a rich text editor using Lit web components, covering setup, dependencies, and code implementation.</description><pubDate>Sun, 19 Jan 2025 07:04:52 GMT</pubDate><content:encoded>&lt;h1&gt;Building a Rich Text Editor with Lit&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to create a rich text editor.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/lit-html-editor&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/lit-html-editor/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;lit-rich-text-editor&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd lit-rich-text-editor
npm i @material/mwc-icon-button
npm i -D @types/node
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &apos;/lit-rich-text-editor/&apos;,
  build: {
    lib: {
      entry: &amp;quot;src/lit-rich-text-editor.ts&amp;quot;,
      formats: [&amp;quot;es&amp;quot;],
    },
    rollupOptions: {
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
    },
  },
});

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;link
      href=&amp;quot;https://fonts.googleapis.com/css?family=Material+Icons&amp;amp;display=block&amp;quot;
      rel=&amp;quot;stylesheet&amp;quot;
    /&amp;gt;
    &amp;lt;title&amp;gt;Lit Rich Text Editor&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/lit-rich-text-editor.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        padding: 0;
        margin: 0;
      }
      lit-rich-text-editor {
        --editor-width: 100%;
        --editor-height: 100vh;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;lit-rich-text-editor&amp;gt;
     &amp;lt;template&amp;gt;
        &amp;lt;h1&amp;gt;Headline 1&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;This is a paragraph.&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;
          &amp;lt;span style=&amp;quot;background-color: rgb(255, 0, 0)&amp;quot;
            &amp;gt;&amp;lt;font color=&amp;quot;#ffffff&amp;quot;&amp;gt;Styled Text&amp;lt;/font&amp;gt;&amp;lt;/span
          &amp;gt;
        &amp;lt;/p&amp;gt;

        &amp;lt;p&amp;gt;
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        &amp;lt;/p&amp;gt;
      &amp;lt;/template&amp;gt;
    &amp;lt;/lit-rich-text-editor&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important things to take away are the styles added to remove the body padding and send size &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/--*&quot;&gt;CSS Custom Properties&lt;/a&gt; to the editor to take up the full viewport.&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;lit-rich-text-editor&lt;/code&gt; tags there is a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template&quot;&gt;&lt;code&gt;template&lt;/code&gt;&lt;/a&gt; passed as a slot to provide html that will not be rendered but can be accessed.&lt;/p&gt;
&lt;p&gt;There is also an import for the &lt;a href=&quot;https://fonts.google.com/icons&quot;&gt;Material Icons&lt;/a&gt; so it can be used in the editor later.&lt;/p&gt;
&lt;h2&gt;Editor&lt;/h2&gt;
&lt;p&gt;The next thing to create is the editor itself. Open up &lt;code&gt;src/lit-rich-text-editor.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property, state } from &amp;quot;lit/decorators.js&amp;quot;;

import &amp;quot;@material/mwc-icon-button&amp;quot;;

@customElement(&amp;quot;lit-rich-text-editor&amp;quot;)
export class LitRichTextEditor extends LitElement {
  @state() content: string = &amp;quot;&amp;quot;;
  @state() root: Element | null = null;

  static styles = css`
    :host {
      --editor-width: 600px;
      --editor-height: 600px;
      --editor-background: #f1f1f1;
      --editor-toolbar-height: 33px;
      --editor-toolbar-background: black;
      --editor-toolbar-on-background: white;
      --editor-toolbar-on-active-background: #a4a4a4;
    }
    main {
      width: var(--editor-width);
      height: var(--editor-height);
      display: grid;
      grid-template-areas:
        &amp;quot;toolbar toolbar&amp;quot;
        &amp;quot;editor editor&amp;quot;;
      grid-template-rows: var(--editor-toolbar-height) auto;
      grid-template-columns: auto auto;
    }
    #editor-actions {
      grid-area: toolbar;
      width: var(--editor-width);
      height: var(--editor-toolbar-height);
      background-color: var(--editor-toolbar-background);
      color: var(--editor-toolbar-on-background);
      overscroll-behavior: contain;
      overflow-y: auto;
      -ms-overflow-style: none;
      scrollbar-width: none;
    }
    #editor-actions::-webkit-scrollbar {
      display: none;
    }
    #editor {
      width: var(--editor-width);
      grid-area: editor;
      background-color: var(--editor-background);
    }
    #toolbar {
      width: 1090px;
      height: var(--editor-toolbar-height);
    }
    [contenteditable] {
      outline: 0px solid transparent;
    }
    #toolbar &amp;gt; mwc-icon-button {
      color: var(--editor-toolbar-on-background);
      --mdc-icon-size: 20px;
      --mdc-icon-button-size: 30px;
      cursor: pointer;
    }
    #toolbar &amp;gt; .active {
      color: var(--editor-toolbar-on-active-background);
    }
    select {
      margin-top: 5px;
      height: calc(var(--editor-toolbar-height) - 10px);
    }
    input[type=&amp;quot;color&amp;quot;] {
      height: calc(var(--editor-toolbar-height) - 15px);
      -webkit-appearance: none;
      border: none;
      width: 22px;
    }
    input[type=&amp;quot;color&amp;quot;]::-webkit-color-swatch-wrapper {
      padding: 0;
    }
    input[type=&amp;quot;color&amp;quot;]::-webkit-color-swatch {
      border: none;
    }
  `;

  render() {
    return html`&amp;lt;main&amp;gt;
      &amp;lt;input id=&amp;quot;bg&amp;quot; type=&amp;quot;color&amp;quot; style=&amp;quot;display:none&amp;quot; /&amp;gt;
      &amp;lt;input id=&amp;quot;fg&amp;quot; type=&amp;quot;color&amp;quot; style=&amp;quot;display:none&amp;quot; /&amp;gt;
      &amp;lt;div id=&amp;quot;editor-actions&amp;quot;&amp;gt;
        &amp;lt;div id=&amp;quot;toolbar&amp;quot;&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div id=&amp;quot;editor&amp;quot;&amp;gt;${this.root}&amp;lt;/div&amp;gt;
    &amp;lt;/main&amp;gt; `;
  }

  async firstUpdated() {
    const elem = this.parentElement!.querySelector(&amp;quot;lit-rich-text-editor template&amp;quot;);
    this.content = elem?.innerHTML ?? &amp;quot;&amp;quot;;
    this.reset();
  }

  reset() {
    const parser = new DOMParser();
    const doc = parser.parseFromString(this.content, &amp;quot;text/html&amp;quot;);
    document.execCommand(&amp;quot;defaultParagraphSeparator&amp;quot;, false, &amp;quot;br&amp;quot;);
    document.addEventListener(&amp;quot;selectionchange&amp;quot;, () =&amp;gt; {
      this.requestUpdate();
    });
    const root = doc.querySelector(&amp;quot;body&amp;quot;);
    root!.setAttribute(&amp;quot;contenteditable&amp;quot;, &amp;quot;true&amp;quot;);
    this.root = root;
  }

}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With everything updated run &lt;code&gt;npm run dev&lt;/code&gt; and the following should appear in the browser:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/lit_piano_1_32gx6viodw.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Nothing special is happening yet, but the template is being read and passed into the element, parsed and setting the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable&quot;&gt;&lt;code&gt;contenteditable&lt;/code&gt;&lt;/a&gt; attribute to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is a way to access the slots and use the nodes to hold data that are not used for rendering. Doing it this way allows for a transformation of the HTML source into a format that can be used.&lt;/p&gt;
&lt;h2&gt;Toolbar&lt;/h2&gt;
&lt;p&gt;At the bottom of the class before the last &lt;code&gt;}&lt;/code&gt; add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;renderToolbar(command: (c: string, val: string | undefined) =&amp;gt; void) {
    // TODO: Selection does not work on Safari iOS
    const selection = this.shadowRoot?.getSelection
      ? this.shadowRoot!.getSelection()
      : null;
    const tags: string[] = [];
    if (selection?.type === &amp;quot;Range&amp;quot;) {
      // @ts-ignore
      let parentNode = selection?.baseNode;
      if (parentNode) {
        const checkNode = () =&amp;gt; {
          const parentTagName = parentNode?.tagName?.toLowerCase()?.trim();
          if (parentTagName) tags.push(parentTagName);
        };
        while (parentNode != null) {
          checkNode();
          parentNode = parentNode?.parentNode;
        }
      }
    }

    const commands: {
      icon: string;
      command: string | (() =&amp;gt; void);
      active?: boolean;
      type?: string;
      values?: { value: string; name: string; font?: boolean }[];
      command_value?: string;
    }[] = [
      {
        icon: &amp;quot;format_clear&amp;quot;,
        command: &amp;quot;removeFormat&amp;quot;,
      },

      {
        icon: &amp;quot;format_bold&amp;quot;,
        command: &amp;quot;bold&amp;quot;,
        active: tags.includes(&amp;quot;b&amp;quot;),
      },
      {
        icon: &amp;quot;format_italic&amp;quot;,
        command: &amp;quot;italic&amp;quot;,
        active: tags.includes(&amp;quot;i&amp;quot;),
      },
      {
        icon: &amp;quot;format_underlined&amp;quot;,
        command: &amp;quot;underline&amp;quot;,
        active: tags.includes(&amp;quot;u&amp;quot;),
      },
      {
        icon: &amp;quot;format_align_left&amp;quot;,
        command: &amp;quot;justifyleft&amp;quot;,
      },
      {
        icon: &amp;quot;format_align_center&amp;quot;,
        command: &amp;quot;justifycenter&amp;quot;,
      },
      {
        icon: &amp;quot;format_align_right&amp;quot;,
        command: &amp;quot;justifyright&amp;quot;,
      },
      {
        icon: &amp;quot;format_list_numbered&amp;quot;,
        command: &amp;quot;insertorderedlist&amp;quot;,
        active: tags.includes(&amp;quot;ol&amp;quot;),
      },
      {
        icon: &amp;quot;format_list_bulleted&amp;quot;,
        command: &amp;quot;insertunorderedlist&amp;quot;,
        active: tags.includes(&amp;quot;ul&amp;quot;),
      },
      {
        icon: &amp;quot;format_quote&amp;quot;,
        command: &amp;quot;formatblock&amp;quot;,
        command_value: &amp;quot;blockquote&amp;quot;,
      },
      {
        icon: &amp;quot;format_indent_decrease&amp;quot;,
        command: &amp;quot;outdent&amp;quot;,
      },
      {
        icon: &amp;quot;format_indent_increase&amp;quot;,
        command: &amp;quot;indent&amp;quot;,
      },

      {
        icon: &amp;quot;add_link&amp;quot;,
        command: () =&amp;gt; {
          const newLink = prompt(&amp;quot;Write the URL here&amp;quot;, &amp;quot;http://&amp;quot;);
          if (newLink &amp;amp;&amp;amp; newLink != &amp;quot;&amp;quot; &amp;amp;&amp;amp; newLink != &amp;quot;http://&amp;quot;) {
            command(&amp;quot;createlink&amp;quot;, newLink);
          }
        },
      },
      { icon: &amp;quot;link_off&amp;quot;, command: &amp;quot;unlink&amp;quot; },
      {
        icon: &amp;quot;format_color_text&amp;quot;,
        command: () =&amp;gt; {
          const input = this.shadowRoot!.querySelector(
            &amp;quot;#fg&amp;quot;
          )! as HTMLInputElement;
          input.addEventListener(&amp;quot;input&amp;quot;, (e: any) =&amp;gt; {
            const val = e.target.value;
            command(&amp;quot;forecolor&amp;quot;, val);
          });
          input.click();
        },
        type: &amp;quot;color&amp;quot;,
      },
      {
        icon: &amp;quot;border_color&amp;quot;,
        command: () =&amp;gt; {
          const input = this.shadowRoot!.querySelector(
            &amp;quot;#bg&amp;quot;
          )! as HTMLInputElement;
          input.addEventListener(&amp;quot;input&amp;quot;, (e: any) =&amp;gt; {
            const val = e.target.value;
            command(&amp;quot;backcolor&amp;quot;, val);
          });
          input.click();
        },
        type: &amp;quot;color&amp;quot;,
      },
      {
        icon: &amp;quot;title&amp;quot;,
        command: &amp;quot;formatblock&amp;quot;,
        values: [
          { name: &amp;quot;Normal Text&amp;quot;, value: &amp;quot;--&amp;quot; },
          { name: &amp;quot;Heading 1&amp;quot;, value: &amp;quot;h1&amp;quot; },
          { name: &amp;quot;Heading 2&amp;quot;, value: &amp;quot;h2&amp;quot; },
          { name: &amp;quot;Heading 3&amp;quot;, value: &amp;quot;h3&amp;quot; },
          { name: &amp;quot;Heading 4&amp;quot;, value: &amp;quot;h4&amp;quot; },
          { name: &amp;quot;Heading 5&amp;quot;, value: &amp;quot;h5&amp;quot; },
          { name: &amp;quot;Heading 6&amp;quot;, value: &amp;quot;h6&amp;quot; },
          { name: &amp;quot;Paragraph&amp;quot;, value: &amp;quot;p&amp;quot; },
          { name: &amp;quot;Pre-Formatted&amp;quot;, value: &amp;quot;pre&amp;quot; },
        ],
      },
      {
        icon: &amp;quot;text_format&amp;quot;,
        command: &amp;quot;fontname&amp;quot;,
        values: [
          { name: &amp;quot;Font Name&amp;quot;, value: &amp;quot;--&amp;quot; },
          ...[...checkFonts()].map((f) =&amp;gt; ({
            name: f,
            value: f,
            font: true,
          })),
        ],
      },
      {
        icon: &amp;quot;format_size&amp;quot;,
        command: &amp;quot;fontsize&amp;quot;,
        values: [
          { name: &amp;quot;Font Size&amp;quot;, value: &amp;quot;--&amp;quot; },
          { name: &amp;quot;Very Small&amp;quot;, value: &amp;quot;1&amp;quot; },
          { name: &amp;quot;Small&amp;quot;, value: &amp;quot;2&amp;quot; },
          { name: &amp;quot;Normal&amp;quot;, value: &amp;quot;3&amp;quot; },
          { name: &amp;quot;Medium Large&amp;quot;, value: &amp;quot;4&amp;quot; },
          { name: &amp;quot;Large&amp;quot;, value: &amp;quot;5&amp;quot; },
          { name: &amp;quot;Very Large&amp;quot;, value: &amp;quot;6&amp;quot; },
          { name: &amp;quot;Maximum&amp;quot;, value: &amp;quot;7&amp;quot; },
        ],
      },
      {
        icon: &amp;quot;undo&amp;quot;,
        command: &amp;quot;undo&amp;quot;,
      },
      {
        icon: &amp;quot;redo&amp;quot;,
        command: &amp;quot;redo&amp;quot;,
      },
      {
        icon: &amp;quot;content_cut&amp;quot;,
        command: &amp;quot;cut&amp;quot;,
      },
      {
        icon: &amp;quot;content_copy&amp;quot;,
        command: &amp;quot;copy&amp;quot;,
      },
      {
        icon: &amp;quot;content_paste&amp;quot;,
        command: &amp;quot;paste&amp;quot;,
      },
    ];

    return html`
      ${commands.map((n) =&amp;gt; {
        return html`
          ${n.values
            ? html` &amp;lt;select
                id=&amp;quot;${n.icon}&amp;quot;
                @change=${(e: any) =&amp;gt; {
                  const val = e.target.value;
                  if (val === &amp;quot;--&amp;quot;) {
                    command(&amp;quot;removeFormat&amp;quot;, undefined);
                  } else if (typeof n.command === &amp;quot;string&amp;quot;) {
                    command(n.command, val);
                  }
                }}
              &amp;gt;
                ${n.values.map(
                  (v) =&amp;gt; html` &amp;lt;option value=${v.value}&amp;gt;${v.name}&amp;lt;/option&amp;gt;`
                )}
              &amp;lt;/select&amp;gt;`
            : html` &amp;lt;mwc-icon-button
                icon=&amp;quot;${n.icon}&amp;quot;
                class=&amp;quot;${n.active ? &amp;quot;active&amp;quot; : &amp;quot;inactive&amp;quot;}&amp;quot;
                @click=${() =&amp;gt; {
                  if (n.values) {
                  } else if (typeof n.command === &amp;quot;string&amp;quot;) {
                    command(n.command, n.command_value);
                  } else {
                    n.command();
                  }
                }}
              &amp;gt;&amp;lt;/mwc-icon-button&amp;gt;`}
        `;
      })}
    `;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This takes an array of objects that we can map to &lt;code&gt;mwc-icon-button&lt;/code&gt; or &lt;code&gt;select&lt;/code&gt; depending on the passed values. This will also set up the event listeners and execute the command for the given action.&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;&amp;lt;div id=&amp;quot;toolbar&amp;quot;&amp;gt;&lt;/code&gt; tag add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;${this.renderToolbar((command, val) =&amp;gt; {
    document.execCommand(command, false, val);
    console.log(&amp;quot;command&amp;quot;, command, val);
})}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will listen for the callback and fire the command on the document and log it to the console.&lt;/p&gt;
&lt;p&gt;And finally at the bottom of the file add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export function checkFonts(): string[] {
  const fontCheck = new Set(
    [
      // Windows 10
      &amp;quot;Arial&amp;quot;,
      &amp;quot;Arial Black&amp;quot;,
      &amp;quot;Bahnschrift&amp;quot;,
      &amp;quot;Calibri&amp;quot;,
      &amp;quot;Cambria&amp;quot;,
      &amp;quot;Cambria Math&amp;quot;,
      &amp;quot;Candara&amp;quot;,
      &amp;quot;Comic Sans MS&amp;quot;,
      &amp;quot;Consolas&amp;quot;,
      &amp;quot;Constantia&amp;quot;,
      &amp;quot;Corbel&amp;quot;,
      &amp;quot;Courier New&amp;quot;,
      &amp;quot;Ebrima&amp;quot;,
      &amp;quot;Franklin Gothic Medium&amp;quot;,
      &amp;quot;Gabriola&amp;quot;,
      &amp;quot;Gadugi&amp;quot;,
      &amp;quot;Georgia&amp;quot;,
      &amp;quot;HoloLens MDL2 Assets&amp;quot;,
      &amp;quot;Impact&amp;quot;,
      &amp;quot;Ink Free&amp;quot;,
      &amp;quot;Javanese Text&amp;quot;,
      &amp;quot;Leelawadee UI&amp;quot;,
      &amp;quot;Lucida Console&amp;quot;,
      &amp;quot;Lucida Sans Unicode&amp;quot;,
      &amp;quot;Malgun Gothic&amp;quot;,
      &amp;quot;Marlett&amp;quot;,
      &amp;quot;Microsoft Himalaya&amp;quot;,
      &amp;quot;Microsoft JhengHei&amp;quot;,
      &amp;quot;Microsoft New Tai Lue&amp;quot;,
      &amp;quot;Microsoft PhagsPa&amp;quot;,
      &amp;quot;Microsoft Sans Serif&amp;quot;,
      &amp;quot;Microsoft Tai Le&amp;quot;,
      &amp;quot;Microsoft YaHei&amp;quot;,
      &amp;quot;Microsoft Yi Baiti&amp;quot;,
      &amp;quot;MingLiU-ExtB&amp;quot;,
      &amp;quot;Mongolian Baiti&amp;quot;,
      &amp;quot;MS Gothic&amp;quot;,
      &amp;quot;MV Boli&amp;quot;,
      &amp;quot;Myanmar Text&amp;quot;,
      &amp;quot;Nirmala UI&amp;quot;,
      &amp;quot;Palatino Linotype&amp;quot;,
      &amp;quot;Segoe MDL2 Assets&amp;quot;,
      &amp;quot;Segoe Print&amp;quot;,
      &amp;quot;Segoe Script&amp;quot;,
      &amp;quot;Segoe UI&amp;quot;,
      &amp;quot;Segoe UI Historic&amp;quot;,
      &amp;quot;Segoe UI Emoji&amp;quot;,
      &amp;quot;Segoe UI Symbol&amp;quot;,
      &amp;quot;SimSun&amp;quot;,
      &amp;quot;Sitka&amp;quot;,
      &amp;quot;Sylfaen&amp;quot;,
      &amp;quot;Symbol&amp;quot;,
      &amp;quot;Tahoma&amp;quot;,
      &amp;quot;Times New Roman&amp;quot;,
      &amp;quot;Trebuchet MS&amp;quot;,
      &amp;quot;Verdana&amp;quot;,
      &amp;quot;Webdings&amp;quot;,
      &amp;quot;Wingdings&amp;quot;,
      &amp;quot;Yu Gothic&amp;quot;,
      // macOS
      &amp;quot;American Typewriter&amp;quot;,
      &amp;quot;Andale Mono&amp;quot;,
      &amp;quot;Arial&amp;quot;,
      &amp;quot;Arial Black&amp;quot;,
      &amp;quot;Arial Narrow&amp;quot;,
      &amp;quot;Arial Rounded MT Bold&amp;quot;,
      &amp;quot;Arial Unicode MS&amp;quot;,
      &amp;quot;Avenir&amp;quot;,
      &amp;quot;Avenir Next&amp;quot;,
      &amp;quot;Avenir Next Condensed&amp;quot;,
      &amp;quot;Baskerville&amp;quot;,
      &amp;quot;Big Caslon&amp;quot;,
      &amp;quot;Bodoni 72&amp;quot;,
      &amp;quot;Bodoni 72 Oldstyle&amp;quot;,
      &amp;quot;Bodoni 72 Smallcaps&amp;quot;,
      &amp;quot;Bradley Hand&amp;quot;,
      &amp;quot;Brush Script MT&amp;quot;,
      &amp;quot;Chalkboard&amp;quot;,
      &amp;quot;Chalkboard SE&amp;quot;,
      &amp;quot;Chalkduster&amp;quot;,
      &amp;quot;Charter&amp;quot;,
      &amp;quot;Cochin&amp;quot;,
      &amp;quot;Comic Sans MS&amp;quot;,
      &amp;quot;Copperplate&amp;quot;,
      &amp;quot;Courier&amp;quot;,
      &amp;quot;Courier New&amp;quot;,
      &amp;quot;Didot&amp;quot;,
      &amp;quot;DIN Alternate&amp;quot;,
      &amp;quot;DIN Condensed&amp;quot;,
      &amp;quot;Futura&amp;quot;,
      &amp;quot;Geneva&amp;quot;,
      &amp;quot;Georgia&amp;quot;,
      &amp;quot;Gill Sans&amp;quot;,
      &amp;quot;Helvetica&amp;quot;,
      &amp;quot;Helvetica Neue&amp;quot;,
      &amp;quot;Herculanum&amp;quot;,
      &amp;quot;Hoefler Text&amp;quot;,
      &amp;quot;Impact&amp;quot;,
      &amp;quot;Lucida Grande&amp;quot;,
      &amp;quot;Luminari&amp;quot;,
      &amp;quot;Marker Felt&amp;quot;,
      &amp;quot;Menlo&amp;quot;,
      &amp;quot;Microsoft Sans Serif&amp;quot;,
      &amp;quot;Monaco&amp;quot;,
      &amp;quot;Noteworthy&amp;quot;,
      &amp;quot;Optima&amp;quot;,
      &amp;quot;Palatino&amp;quot;,
      &amp;quot;Papyrus&amp;quot;,
      &amp;quot;Phosphate&amp;quot;,
      &amp;quot;Rockwell&amp;quot;,
      &amp;quot;Savoye LET&amp;quot;,
      &amp;quot;SignPainter&amp;quot;,
      &amp;quot;Skia&amp;quot;,
      &amp;quot;Snell Roundhand&amp;quot;,
      &amp;quot;Tahoma&amp;quot;,
      &amp;quot;Times&amp;quot;,
      &amp;quot;Times New Roman&amp;quot;,
      &amp;quot;Trattatello&amp;quot;,
      &amp;quot;Trebuchet MS&amp;quot;,
      &amp;quot;Verdana&amp;quot;,
      &amp;quot;Zapfino&amp;quot;,
    ].sort()
  );
  const fontAvailable = new Set&amp;lt;string&amp;gt;();
  // @ts-ignore
  for (const font of fontCheck.values()) {
    // @ts-ignore
    if (document.fonts.check(`12px &amp;quot;${font}&amp;quot;`)) {
      fontAvailable.add(font);
    }
  }
  // @ts-ignore
  return fontAvailable.values();
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this great suggestion &lt;a href=&quot;https://stackoverflow.com/a/62755574/7303311&quot;&gt;here&lt;/a&gt; the document checks to see all the avaliable fonts for the browser and given document.&lt;/p&gt;
&lt;h2&gt;Running&lt;/h2&gt;
&lt;p&gt;If everything went well when the command &lt;code&gt;npm run dev&lt;/code&gt; is run the following should appear in the viewport:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/lit_text_2_y8vvurvfh1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building with Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source for this example can be found &lt;a href=&quot;https://github.com/rodydavis/lit-html-editor&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>How to do Full Text Search with SQLite</title><link>https://rodydavis.com/sqlite/fts5/</link><guid isPermaLink="true">https://rodydavis.com/sqlite/fts5/</guid><description>SQLite enables advanced full-text search (FTS) functionality through the fts5 extension and virtual tables, allowing users to efficiently query large datasets using the `MATCH` keyword.</description><pubDate>Sat, 18 Jan 2025 20:49:31 GMT</pubDate><content:encoded>&lt;h1&gt;How to do Full Text Search with SQLite&lt;/h1&gt;
&lt;p&gt;SQLite has a powerful way to add new functionality via &lt;a href=&quot;https://www.sqlite.org/loadext.html&quot;&gt;loadable extensions&lt;/a&gt;. The first-party ones include &lt;a href=&quot;https://www.sqlite.org/fts5.html&quot;&gt;fts5&lt;/a&gt;, &lt;a href=&quot;https://www.sqlite.org/json1.html&quot;&gt;json1&lt;/a&gt; and a couple others.&lt;/p&gt;
&lt;p&gt;When building applications it is common to add searching features based on data coming from tables and you may already have queries for fuzzy searching with &lt;strong&gt;LIKE&lt;/strong&gt;. You may be excited to hear that SQLite can easily add fully query capabilities over a dataset all with just a simple &lt;strong&gt;MATCH&lt;/strong&gt; keyword. 👀&lt;/p&gt;
&lt;h2&gt;Creating your first search index&lt;/h2&gt;
&lt;p&gt;Full text search in SQLite requires storing the index in &lt;strong&gt;VIRTUAL&lt;/strong&gt; tables, which allow for optimized storage of the index based on the queries we will execute against it.&lt;/p&gt;
&lt;p&gt;You can create the virtual table for the index making sure to include the &lt;strong&gt;USING&lt;/strong&gt; directive for the fts5 target.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE VIRTUAL TABLE posts_fts USING fts5 (
    title,
    description,
    content,
    content=posts,
    content_rowid=id
);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Text IDs are also supported instead of just INTEGERS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a standard callout. You can customize its content and even the icon.&lt;/p&gt;
&lt;h3&gt;Contentless tables&lt;/h3&gt;
&lt;p&gt;You can also create a contentless table that will not be based on any existing tables:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE VIRTUAL TABLE example_fts USING fts5 (
    name,
    description,
    content=&apos;&apos;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Keeping the index up to date&lt;/h2&gt;
&lt;p&gt;By having the source content be stored in another table we need to make sure to keep both tables in sync and avoid updating the index in a hot path when trying to make a query.&lt;/p&gt;
&lt;p&gt;By default when you create table it will be empty, even if the source table is populated. You do have various options for populating the index.&lt;/p&gt;
&lt;h3&gt;Update by query&lt;/h3&gt;
&lt;p&gt;If you use a contentless table or want to pull in data from a view you can update by query.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT INTO posts_fts (id, title, description, content)
SELECT id, title, description, content FROM posts;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Rebuild command&lt;/h3&gt;
&lt;p&gt;Using the rebuild command it will update the index based on the content table specified.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT INTO posts_fts(posts_fts) VALUES(&apos;rebuild&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Triggers&lt;/h3&gt;
&lt;p&gt;We can use SQLite triggers to automatically keep the records updated:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TRIGGER posts_insert AFTER INSERT ON posts BEGIN
  INSERT INTO posts_fts(id, title, description, content)
  VALUES (new.id, new.title, new.description, new.content);
END;

CREATE TRIGGER posts_delete AFTER DELETE ON posts BEGIN
  INSERT INTO posts_fts(posts_fts, id, title, description, content)
  VALUES (&apos;delete&apos;, old.id, old.title, old.description, old.content);
END;

CREATE TRIGGER posts_update AFTER UPDATE ON posts BEGIN
  INSERT INTO posts_fts(posts_fts, id, title, description, content)
  VALUES (&apos;delete&apos;, old.id, old.title, old.description, old.content);
  INSERT INTO posts_fts(id, title, description, content)
  VALUES (new.id, new.title, new.description, new.content);
END;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will always ensure the two tables are in sync for any CRUD actions on the source table.&lt;/p&gt;
&lt;h2&gt;Searching the index&lt;/h2&gt;
&lt;h3&gt;Query syntax&lt;/h3&gt;
&lt;p&gt;Here is the supported query syntax:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&amp;lt;phrase&amp;gt;    := string [*]
&amp;lt;phrase&amp;gt;    := &amp;lt;phrase&amp;gt; + &amp;lt;phrase&amp;gt;
&amp;lt;neargroup&amp;gt; := NEAR ( &amp;lt;phrase&amp;gt; &amp;lt;phrase&amp;gt; ... [, N] )
&amp;lt;query&amp;gt;     := [ [-] &amp;lt;colspec&amp;gt; :] [^] &amp;lt;phrase&amp;gt;
&amp;lt;query&amp;gt;     := [ [-] &amp;lt;colspec&amp;gt; :] &amp;lt;neargroup&amp;gt;
&amp;lt;query&amp;gt;     := [ [-] &amp;lt;colspec&amp;gt; :] ( &amp;lt;query&amp;gt; )
&amp;lt;query&amp;gt;     := &amp;lt;query&amp;gt; AND &amp;lt;query&amp;gt;
&amp;lt;query&amp;gt;     := &amp;lt;query&amp;gt; OR &amp;lt;query&amp;gt;
&amp;lt;query&amp;gt;     := &amp;lt;query&amp;gt; NOT &amp;lt;query&amp;gt;
&amp;lt;colspec&amp;gt;   := colname
&amp;lt;colspec&amp;gt;   := { colname1 colname2 ... }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To preform an actual query on the index we will need to use the &lt;strong&gt;MATCH&lt;/strong&gt; keyword and order by the rank.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT posts.* FROM posts_fts
INNER JOIN posts ON posts.id = posts_fts.rowid
WHERE posts_fts MATCH :query
ORDER BY rank;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sqlite.org/fts5.html&quot;&gt;https://www.sqlite.org/fts5.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.datasette.io/en/latest/full_text_search.html&quot;&gt;https://docs.datasette.io/en/latest/full_text_search.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Lit and VSCode Extensions</title><link>https://rodydavis.com/lit/vscode-extension/</link><guid isPermaLink="true">https://rodydavis.com/lit/vscode-extension/</guid><description>A guide detailing the steps to set up a Lit web component and integrate it into a functional VSCode extension using Vite and TypeScript.</description><pubDate>Mon, 20 Jan 2025 02:41:17 GMT</pubDate><content:encoded>&lt;h1&gt;Lit and VSCode Extensions&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to create a VSCode extension.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; You can find the final source &lt;a href=&quot;https://github.com/rodydavis/lit-vscode-extension&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;lit-vscode-extension&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd lit-vscode-extension
npm i -D @types/node
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &amp;quot;/lit-vscode-extension/&amp;quot;,
  build: {
    outDir: &amp;quot;build&amp;quot;,
    rollupOptions: {
      external: /^vscode/,
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
      output: {
        entryFileNames: &amp;quot;[name].js&amp;quot;,
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;package.json&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &amp;quot;name&amp;quot;: &amp;quot;lit-vscode-extension&amp;quot;,
  &amp;quot;description&amp;quot;: &amp;quot;Lit VSCode Extension Example&amp;quot;,
  &amp;quot;version&amp;quot;: &amp;quot;0.0.1&amp;quot;,
  &amp;quot;publisher&amp;quot;: &amp;quot;rodydavis&amp;quot;,
  &amp;quot;private&amp;quot;: true,
  &amp;quot;license&amp;quot;: &amp;quot;MIT&amp;quot;,
  &amp;quot;repository&amp;quot;: {
    &amp;quot;type&amp;quot;: &amp;quot;git&amp;quot;,
    &amp;quot;url&amp;quot;: &amp;quot;https://github.com/rodydavis/lit-vscode-extension&amp;quot;
  },
  &amp;quot;engines&amp;quot;: {
    &amp;quot;vscode&amp;quot;: &amp;quot;^1.47.0&amp;quot;
  },
  &amp;quot;categories&amp;quot;: [
    &amp;quot;Other&amp;quot;
  ],
  &amp;quot;activationEvents&amp;quot;: [
    &amp;quot;onCommand:lit.start&amp;quot;,
    &amp;quot;onCommand:lit.reset&amp;quot;,
    &amp;quot;onWebviewPanel:lit&amp;quot;
  ],
  &amp;quot;main&amp;quot;: &amp;quot;./build/extension.js&amp;quot;,
  &amp;quot;contributes&amp;quot;: {
    &amp;quot;commands&amp;quot;: [
      {
        &amp;quot;command&amp;quot;: &amp;quot;lit.start&amp;quot;,
        &amp;quot;title&amp;quot;: &amp;quot;Open Plugin&amp;quot;,
        &amp;quot;category&amp;quot;: &amp;quot;lit&amp;quot;
      },
      {
        &amp;quot;command&amp;quot;: &amp;quot;lit.reset&amp;quot;,
        &amp;quot;title&amp;quot;: &amp;quot;Reset&amp;quot;,
        &amp;quot;category&amp;quot;: &amp;quot;lit&amp;quot;
      }
    ]
  },
  &amp;quot;scripts&amp;quot;: {
    &amp;quot;dev&amp;quot;: &amp;quot;vite&amp;quot;,
    &amp;quot;build&amp;quot;: &amp;quot;tsc &amp;amp;&amp;amp; vite build&amp;quot;,
    &amp;quot;ext&amp;quot;: &amp;quot;tsc src/extension.ts --outdir build --skipLibCheck --module commonjs&amp;quot;,
    &amp;quot;compile&amp;quot;: &amp;quot;npm run build &amp;amp;&amp;amp; npm run ext&amp;quot;,
    &amp;quot;serve&amp;quot;: &amp;quot;vite preview&amp;quot;
  },
  &amp;quot;dependencies&amp;quot;: {
    &amp;quot;lit&amp;quot;: &amp;quot;^2.0.0-rc.2&amp;quot;
  },
  &amp;quot;devDependencies&amp;quot;: {
    &amp;quot;@types/node&amp;quot;: &amp;quot;^15.12.4&amp;quot;,
    &amp;quot;@types/vscode&amp;quot;: &amp;quot;^1.57.0&amp;quot;,
    &amp;quot;typescript&amp;quot;: &amp;quot;^4.2.3&amp;quot;,
    &amp;quot;vite&amp;quot;: &amp;quot;^2.3.5&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the &lt;code&gt;package.json&lt;/code&gt; is updated run &lt;code&gt;npm i&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Lit Example&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/my-element.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;my-element&amp;gt;
      &amp;lt;p&amp;gt;This is child content&amp;lt;/p&amp;gt;
    &amp;lt;/my-element&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Web Component&lt;/h2&gt;
&lt;p&gt;Open up &lt;code&gt;src/my-element.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property, state } from &amp;quot;lit/decorators.js&amp;quot;;

@customElement(&amp;quot;my-element&amp;quot;)
export class MyElement extends LitElement {
  static styles = css`
    :host {
      display: block;
      border: solid 1px gray;
      padding: 16px;
      max-width: 800px;
    }
  `;

  @property() name = &amp;quot;World&amp;quot;;
  @state() count = 0;

  render() {
    return html`
      &amp;lt;h1&amp;gt;Hello, ${this.name}!&amp;lt;/h1&amp;gt;
      &amp;lt;button @click=${() =&amp;gt; this.modify(1)} part=&amp;quot;button&amp;quot;&amp;gt;
        Click Count: ${this.count}
      &amp;lt;/button&amp;gt;
      &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
    `;
  }

  modify(val: number) {
    this.count += val;
  }

  reset() {
    this.count = 0;
  }

  async firstUpdated() {
    window.addEventListener(
      &amp;quot;message&amp;quot;,
      (e: any) =&amp;gt; {
        const message = e.data;
        const { command } = message;
        if (command === &amp;quot;reset&amp;quot;) {
          this.reset();
        }
      },
      false
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are just modifying the example to include a message listener for communicating with the vscode extension, methods for updating the count, and updating the render method.&lt;/p&gt;
&lt;p&gt;VSCode communicates with the plugin via post messages because the UI will be loaded in an &lt;code&gt;iframe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now let&apos;s write the extension code in &lt;code&gt;src/extension.ts&lt;/code&gt;. First start by adding top level declarations that will be referenced multiple times.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import * as vscode from &amp;quot;vscode&amp;quot;;

const WEB_DIR: string = &amp;quot;build&amp;quot;;
const WEB_SCRIPT: string = &amp;quot;main.js&amp;quot;;
const TITLE: string = &amp;quot;Lit Example&amp;quot;;
const TAG: string = &amp;quot;my-element&amp;quot;;


const possible = [
  &amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;,
  &amp;quot;abcdefghijklmnopqrstuvwxyz&amp;quot;,
  &amp;quot;0123456789&amp;quot;,
].join(&amp;quot;&amp;quot;);

function getNonce() {
  let text = &amp;quot;&amp;quot;;
  for (let i = 0; i &amp;lt; 32; i++) {
    const char = possible.charAt(Math.floor(Math.random() * possible.length));
    text += char;
  }
  return text;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also are creating a &lt;code&gt;getNonce&lt;/code&gt; method that will be used for making sure the scripts we load are the ones we passed in.&lt;/p&gt;
&lt;p&gt;Now create a &lt;code&gt;Panel&lt;/code&gt; that will contain the ui for our plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Panel {
  public static currentPanel: Panel | undefined;
  public static readonly viewType = &amp;quot;litExample&amp;quot;;
  private _disposables: vscode.Disposable[] = [];

  public static createOrShow(extensionUri: vscode.Uri) {
    const column = vscode.window.activeTextEditor
      ? vscode.window.activeTextEditor.viewColumn
      : undefined;
    if (Panel.currentPanel) {
      Panel.currentPanel.panel.reveal(column);
      return;
    }
    const panel = vscode.window.createWebviewPanel(
      Panel.viewType,
      TITLE,
      column || vscode.ViewColumn.One,
      getWebviewOptions(extensionUri)
    );
    Panel.currentPanel = new Panel(panel, extensionUri);
  }

  public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
    Panel.currentPanel = new Panel(panel, extensionUri);
  }

  private constructor(
    public readonly panel: vscode.WebviewPanel,
    public readonly extensionUri: vscode.Uri
  ) {
    this._update();
    this.panel.onDidDispose(() =&amp;gt; this.dispose(), null, this._disposables);
    this.panel.onDidChangeViewState(
      (_) =&amp;gt; {
        if (this.panel.visible) {
          this._update();
        }
      },
      null,
      this._disposables
    );
    this.panel.webview.onDidReceiveMessage(
      (message) =&amp;gt; {
        switch (message.command) {
          case &amp;quot;alert&amp;quot;:
            vscode.window.showErrorMessage(message.text);
            return;
        }
      },
      null,
      this._disposables
    );
  }

  public sendMessage(command: string) {
    this.panel.webview.postMessage({ command: command });
  }

  public dispose() {
    Panel.currentPanel = undefined;
    this.panel.dispose();
    while (this._disposables.length) {
      const x = this._disposables.pop();
      if (x) {
        x.dispose();
      }
    }
  }

  private _update() {
    const webview = this.panel.webview;
    webview.html = this._getHtmlForWebview(webview);
  }

  private _getHtmlForWebview(webview: vscode.Webview) {
    const scriptPathOnDisk = vscode.Uri.joinPath(
      this.extensionUri,
      WEB_DIR,
      WEB_SCRIPT
    );
    const scriptUri = webview.asWebviewUri(scriptPathOnDisk);
    const nonce = getNonce();

    const slot = &amp;quot;&amp;lt;p&amp;gt;This is child content&amp;lt;/p&amp;gt;&amp;quot;;

    return `&amp;lt;!DOCTYPE html&amp;gt;
			&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
        &amp;lt;head&amp;gt;
          &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
          &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;
          &amp;lt;title&amp;gt;${TITLE}&amp;lt;/title&amp;gt;
        &amp;lt;/head&amp;gt;
        &amp;lt;body class=&amp;quot;vscode-light&amp;quot;&amp;gt;
          &amp;lt;${TAG} nonce=&amp;quot;${nonce}&amp;quot; &amp;gt;
            ${slot}
          &amp;lt;/${TAG}&amp;gt;
          &amp;lt;script nonce=&amp;quot;${nonce}&amp;quot; type=&amp;quot;module&amp;quot; src=&amp;quot;${scriptUri}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;/body&amp;gt;
			&amp;lt;/html&amp;gt;`;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice how we are recreating the html and not using the &lt;code&gt;index.html&lt;/code&gt;. This allows us to use the component for a deployed website but also the extension with separate app logic code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Everything else is just boilerplate for managing the panel state and disposing when it is finished.&lt;/p&gt;
&lt;p&gt;Now we can add the methods for loading our plugin and listening for the commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand(&amp;quot;lit.start&amp;quot;, () =&amp;gt; {
      Panel.createOrShow(context.extensionUri);
    })
  );

  context.subscriptions.push(
    vscode.commands.registerCommand(&amp;quot;lit.reset&amp;quot;, () =&amp;gt; {
      if (Panel.currentPanel) {
        Panel.currentPanel.sendMessage(&amp;quot;reset&amp;quot;);
      }
    })
  );

  if (vscode.window.registerWebviewPanelSerializer) {
    vscode.window.registerWebviewPanelSerializer(Panel.viewType, {
      async deserializeWebviewPanel(
        webviewPanel: vscode.WebviewPanel,
        state: any
      ) {
        console.log(`Received state: ${state}`);
        webviewPanel.webview.options = getWebviewOptions(context.extensionUri);
        Panel.revive(webviewPanel, context.extensionUri);
      },
    });
  }
}

function getWebviewOptions(extensionUri: vscode.Uri): vscode.WebviewOptions {
  return {
    enableScripts: true,
    localResourceRoots: [vscode.Uri.joinPath(extensionUri, WEB_DIR)],
  };
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are loading the extension and passing messages when the &lt;code&gt;lit.reset&lt;/code&gt; command. We are also returning the &lt;code&gt;getWebviewOptions&lt;/code&gt; options which &lt;code&gt;enableScripts&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt; so we can run the injected js.&lt;/p&gt;
&lt;p&gt;The final code should look like the follow:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import * as vscode from &amp;quot;vscode&amp;quot;;

const WEB_DIR: string = &amp;quot;build&amp;quot;;
const WEB_SCRIPT: string = &amp;quot;main.js&amp;quot;;
const TITLE: string = &amp;quot;Lit Example&amp;quot;;
const TAG: string = &amp;quot;my-element&amp;quot;;

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand(&amp;quot;lit.start&amp;quot;, () =&amp;gt; {
      Panel.createOrShow(context.extensionUri);
    })
  );

  context.subscriptions.push(
    vscode.commands.registerCommand(&amp;quot;lit.reset&amp;quot;, () =&amp;gt; {
      if (Panel.currentPanel) {
        Panel.currentPanel.sendMessage(&amp;quot;reset&amp;quot;);
      }
    })
  );

  if (vscode.window.registerWebviewPanelSerializer) {
    vscode.window.registerWebviewPanelSerializer(Panel.viewType, {
      async deserializeWebviewPanel(
        webviewPanel: vscode.WebviewPanel,
        state: any
      ) {
        console.log(`Received state: ${state}`);
        webviewPanel.webview.options = getWebviewOptions(context.extensionUri);
        Panel.revive(webviewPanel, context.extensionUri);
      },
    });
  }
}

function getWebviewOptions(extensionUri: vscode.Uri): vscode.WebviewOptions {
  return {
    enableScripts: true,
    localResourceRoots: [vscode.Uri.joinPath(extensionUri, WEB_DIR)],
  };
}

class Panel {
  public static currentPanel: Panel | undefined;
  public static readonly viewType = &amp;quot;litExample&amp;quot;;
  private _disposables: vscode.Disposable[] = [];

  public static createOrShow(extensionUri: vscode.Uri) {
    const column = vscode.window.activeTextEditor
      ? vscode.window.activeTextEditor.viewColumn
      : undefined;
    if (Panel.currentPanel) {
      Panel.currentPanel.panel.reveal(column);
      return;
    }
    const panel = vscode.window.createWebviewPanel(
      Panel.viewType,
      TITLE,
      column || vscode.ViewColumn.One,
      getWebviewOptions(extensionUri)
    );
    Panel.currentPanel = new Panel(panel, extensionUri);
  }

  public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
    Panel.currentPanel = new Panel(panel, extensionUri);
  }

  private constructor(
    public readonly panel: vscode.WebviewPanel,
    public readonly extensionUri: vscode.Uri
  ) {
    this._update();
    this.panel.onDidDispose(() =&amp;gt; this.dispose(), null, this._disposables);
    this.panel.onDidChangeViewState(
      (_) =&amp;gt; {
        if (this.panel.visible) {
          this._update();
        }
      },
      null,
      this._disposables
    );
    this.panel.webview.onDidReceiveMessage(
      (message) =&amp;gt; {
        switch (message.command) {
          case &amp;quot;alert&amp;quot;:
            vscode.window.showErrorMessage(message.text);
            return;
        }
      },
      null,
      this._disposables
    );
  }

  public sendMessage(command: string) {
    this.panel.webview.postMessage({ command: command });
  }

  public dispose() {
    Panel.currentPanel = undefined;
    this.panel.dispose();
    while (this._disposables.length) {
      const x = this._disposables.pop();
      if (x) {
        x.dispose();
      }
    }
  }

  private _update() {
    const webview = this.panel.webview;
    webview.html = this._getHtmlForWebview(webview);
  }

  private _getHtmlForWebview(webview: vscode.Webview) {
    const scriptPathOnDisk = vscode.Uri.joinPath(
      this.extensionUri,
      WEB_DIR,
      WEB_SCRIPT
    );
    const scriptUri = webview.asWebviewUri(scriptPathOnDisk);
    const nonce = getNonce();

    const slot = &amp;quot;&amp;lt;p&amp;gt;This is child content&amp;lt;/p&amp;gt;&amp;quot;;

    const htmlSource = `
    &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
      &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
        &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;
        &amp;lt;title&amp;gt;${TITLE}&amp;lt;/title&amp;gt;
      &amp;lt;/head&amp;gt;
      &amp;lt;body class=&amp;quot;vscode-light&amp;quot;&amp;gt;
        &amp;lt;${TAG} nonce=&amp;quot;${nonce}&amp;quot; &amp;gt;
          ${slot}
        &amp;lt;/${TAG}&amp;gt;
        &amp;lt;script nonce=&amp;quot;${nonce}&amp;quot; type=&amp;quot;module&amp;quot; src=&amp;quot;${scriptUri}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;`;

    return htmlSource;
  }
}

const possible = [
  &amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;,
  &amp;quot;abcdefghijklmnopqrstuvwxyz&amp;quot;,
  &amp;quot;0123456789&amp;quot;,
].join(&amp;quot;&amp;quot;);

function getNonce() {
  let text = &amp;quot;&amp;quot;;
  for (let i = 0; i &amp;lt; 32; i++) {
    const char = possible.charAt(Math.floor(Math.random() * possible.length));
    text += char;
  }
  return text;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Running&lt;/h2&gt;
&lt;p&gt;Make sure to install the dependencies by running &lt;code&gt;npm i&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To build the extension run &lt;code&gt;npm run compile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To open the extension and debug hit &lt;code&gt;F5&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/v_1_lcs00w2hlu.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;To run the commands to open the extension run &lt;code&gt;lit: open plugin&lt;/code&gt; or &lt;code&gt;lit: reset&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/v_2_g5d9uzk1if.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;To debug the extension when it is open run &lt;code&gt;Developer: Open Webview Developer Tools&lt;/code&gt; from the command pallet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/v_3_djp3kxusi7.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building a vscode extension you can read more &lt;a href=&quot;https://code.visualstudio.com/api&quot;&gt;here&lt;/a&gt; and for Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Using SQLite as a Key Value Store</title><link>https://rodydavis.com/sqlite/key-value/</link><guid isPermaLink="true">https://rodydavis.com/sqlite/key-value/</guid><description>Learn how to create and manage a robust key-value store in SQLite using standard SQL commands to perform operations like inserting, retrieving, updating, and deleting data pairs.</description><pubDate>Fri, 17 Jan 2025 06:54:23 GMT</pubDate><content:encoded>&lt;h1&gt;Using SQLite as a Key Value Store&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt; is a very capable edge database that can store various shapes of data.&lt;/p&gt;
&lt;p&gt;Key/Value databases are popular in applications for storing settings, and other non-relational data.&lt;/p&gt;
&lt;p&gt;By using SQLite to store the key/values you can contain all the data for a user in a single file and can &lt;a href=&quot;https://www.sqlite.org/lang_attach.html&quot;&gt;attach it to other databases&lt;/a&gt; or sync it to a server.&lt;/p&gt;
&lt;h2&gt;Create the table&lt;/h2&gt;
&lt;p&gt;To store key/value type data we need to first create our table.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE key_value (
  key TEXT NOT NULL PRIMARY KEY,
  value,
  UNIQUE(key)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;key&lt;/th&gt;
&lt;th&gt;value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;user_id&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;foo&lt;/td&gt;
&lt;td&gt;bar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;active&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;guest&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;SQLite has &lt;a href=&quot;https://www.sqlite.org/datatype3.html&quot;&gt;optional column types&lt;/a&gt; and can be very useful for dynamic values.&lt;/p&gt;
&lt;h2&gt;Save a value&lt;/h2&gt;
&lt;p&gt;To save a value for a given key we can run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT OR REPLACE 
INTO key_value (key, value) 
VALUES (:key, :value)
RETURNING *;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;key&lt;/th&gt;
&lt;th&gt;value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;user_id&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Since the key is &lt;a href=&quot;https://www.sqlitetutorial.net/sqlite-unique-constraint/&quot;&gt;UNIQUE&lt;/a&gt; we do not have to worry about conflicts as it will overwrite the value as intended.&lt;/p&gt;
&lt;h2&gt;Read a value&lt;/h2&gt;
&lt;p&gt;To read a value we can pass in a key to our query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT value FROM key_value 
WHERE key = :key;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;value&lt;/p&gt;
&lt;p&gt;1&lt;/p&gt;
&lt;p&gt;This will only return a single value column with a max of 1 rows.&lt;/p&gt;
&lt;h2&gt;Delete a value&lt;/h2&gt;
&lt;p&gt;To delete a value or key we can run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;DELETE FROM key_value 
WHERE key = :key;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Search for key or value&lt;/h2&gt;
&lt;p&gt;We can also search for a specific key or value (if it is a string) with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT key, value
FROM key_value 
WHERE key LIKE :query 
OR value LIKE :query;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;key&lt;/th&gt;
&lt;th&gt;value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;bar&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;foo&lt;/td&gt;
&lt;td&gt;bar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Drift Support&lt;/h2&gt;
&lt;p&gt;If you are using &lt;a href=&quot;https://drift.simonbinder.eu/&quot;&gt;Drift&lt;/a&gt; in dart, create a new file &lt;code&gt;key_value.drift&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE key_value (
  &amp;quot;key&amp;quot; TEXT NOT NULL PRIMARY KEY,
  value TEXT,
  UNIQUE(&amp;quot;key&amp;quot;)
);

setItem:
INSERT OR REPLACE 
INTO key_value (&amp;quot;key&amp;quot;, value) 
VALUES (:key, :value)
RETURNING *;

getItem:
SELECT value FROM key_value 
WHERE &amp;quot;key&amp;quot; = :key;

deleteItem:
DELETE FROM key_value 
WHERE &amp;quot;key&amp;quot; = :key;

searchItem:
SELECT &amp;quot;key&amp;quot;, value
FROM key_value 
WHERE &amp;quot;key&amp;quot; LIKE :query 
OR value LIKE :query;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Lit Sheet Music</title><link>https://rodydavis.com/lit/sheet-music/</link><guid isPermaLink="true">https://rodydavis.com/lit/sheet-music/</guid><description>Use the Lit web component and OpenSheetMusicDisplay to dynamically render and resize sheet music (MusicXML) from a source or inline XML directly within a web page.</description><pubDate>Mon, 20 Jan 2025 02:46:46 GMT</pubDate><content:encoded>&lt;h1&gt;Lit Sheet Music&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to render &lt;a href=&quot;https://www.musicxml.com/&quot;&gt;musicxml&lt;/a&gt; from a src attribute or inline xml using &lt;a href=&quot;https://github.com/opensheetmusicdisplay/opensheetmusicdisplay&quot;&gt;opensheetmusicdisplay&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/nice_rbhmdu7o6t.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now any sheet music can be rendered based on the browser width as an svg or canvas (and will resize when the viewport changes).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/lit-sheet-music&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/lit-sheet-music/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;lit-sheet-music&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd lit-sheet-music
npm i lit opensheetmusicdisplay
npm i -D @types/node
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &amp;quot;/lit-sheet-music/&amp;quot;,
  build: {
    lib: {
      entry: &amp;quot;src/lit-sheet-music.ts&amp;quot;,
      formats: [&amp;quot;es&amp;quot;],
    },
    rollupOptions: {
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Lit Sheet Music&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/sheet-music.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
        padding: 0;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;sheet-music
      src=&amp;quot;https://raw.githubusercontent.com/opensheetmusicdisplay/opensheetmusicdisplay/develop/demo/BrahWiMeSample.musicxml&amp;quot;
    &amp;gt;
    &amp;lt;/sheet-music&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If local &lt;a href=&quot;https://www.musicxml.com/&quot;&gt;musicxml&lt;/a&gt; is intended to be used update &lt;code&gt;index.html&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Lit Sheet Music&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/sheet-music.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
        padding: 0;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;sheet-music&amp;gt;
      &amp;lt;script type=&amp;quot;text/xml&amp;quot;&amp;gt;
        &amp;lt;?xml version=&amp;quot;1.0&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;
        &amp;lt;!DOCTYPE score-partwise PUBLIC
            &amp;quot;-//Recordare//DTD MusicXML Partwise//EN&amp;quot;
            &amp;quot;http://www.musicxml.org/dtds/partwise.dtd&amp;quot;&amp;gt;
          &amp;lt;score-partwise&amp;gt;
            &amp;lt;part-list&amp;gt;
              &amp;lt;score-part id=&amp;quot;P1&amp;quot;&amp;gt;
                &amp;lt;part-name&amp;gt;Voice&amp;lt;/part-name&amp;gt;
              &amp;lt;/score-part&amp;gt;
            &amp;lt;/part-list&amp;gt;
            &amp;lt;part id=&amp;quot;P1&amp;quot;&amp;gt;
              &amp;lt;measure number=&amp;quot;0&amp;quot; implicit=&amp;quot;yes&amp;quot;&amp;gt;
                &amp;lt;attributes&amp;gt;
                  &amp;lt;divisions&amp;gt;4&amp;lt;/divisions&amp;gt;
                  &amp;lt;key&amp;gt;
                    &amp;lt;fifths&amp;gt;-3&amp;lt;/fifths&amp;gt;
                    &amp;lt;mode&amp;gt;major&amp;lt;/mode&amp;gt;
                  &amp;lt;/key&amp;gt;
                  &amp;lt;time&amp;gt;
                    &amp;lt;beats&amp;gt;2&amp;lt;/beats&amp;gt;
                    &amp;lt;beat-type&amp;gt;4&amp;lt;/beat-type&amp;gt;
                  &amp;lt;/time&amp;gt;
                  &amp;lt;clef&amp;gt;
                    &amp;lt;sign&amp;gt;G&amp;lt;/sign&amp;gt;
                    &amp;lt;line&amp;gt;2&amp;lt;/line&amp;gt;
                  &amp;lt;/clef&amp;gt;
                  &amp;lt;directive&amp;gt;Langsam, innig.&amp;lt;/directive&amp;gt;
                &amp;lt;/attributes&amp;gt;
                &amp;lt;note&amp;gt;
                  &amp;lt;pitch&amp;gt;
                    &amp;lt;step&amp;gt;G&amp;lt;/step&amp;gt;
                    &amp;lt;octave&amp;gt;4&amp;lt;/octave&amp;gt;
                  &amp;lt;/pitch&amp;gt;
                  &amp;lt;duration&amp;gt;2&amp;lt;/duration&amp;gt;
                  &amp;lt;type&amp;gt;eighth&amp;lt;/type&amp;gt;
                  &amp;lt;stem&amp;gt;up&amp;lt;/stem&amp;gt;
                  &amp;lt;notations&amp;gt;
                    &amp;lt;dynamics&amp;gt;
                      &amp;lt;p/&amp;gt;
                    &amp;lt;/dynamics&amp;gt;
                  &amp;lt;/notations&amp;gt;
                  &amp;lt;lyric&amp;gt;
                    &amp;lt;syllabic&amp;gt;single&amp;lt;/syllabic&amp;gt;
                    &amp;lt;text&amp;gt;W&amp;amp;auml;rst&amp;lt;/text&amp;gt;
                  &amp;lt;/lyric&amp;gt;
                &amp;lt;/note&amp;gt;
              &amp;lt;/measure&amp;gt;
              &amp;lt;measure number=&amp;quot;1&amp;quot;&amp;gt;
                &amp;lt;note&amp;gt;
                  &amp;lt;pitch&amp;gt;
                    &amp;lt;step&amp;gt;F&amp;lt;/step&amp;gt;
                    &amp;lt;octave&amp;gt;4&amp;lt;/octave&amp;gt;
                  &amp;lt;/pitch&amp;gt;
                  &amp;lt;duration&amp;gt;3&amp;lt;/duration&amp;gt;
                  &amp;lt;type&amp;gt;eighth&amp;lt;/type&amp;gt;
                  &amp;lt;dot/&amp;gt;
                  &amp;lt;stem&amp;gt;up&amp;lt;/stem&amp;gt;
                  &amp;lt;lyric&amp;gt;
                    &amp;lt;syllabic&amp;gt;single&amp;lt;/syllabic&amp;gt;
                    &amp;lt;text&amp;gt;du&amp;lt;/text&amp;gt;
                  &amp;lt;/lyric&amp;gt;
                &amp;lt;/note&amp;gt;
                &amp;lt;note&amp;gt;
                  &amp;lt;pitch&amp;gt;
                    &amp;lt;step&amp;gt;E&amp;lt;/step&amp;gt;
                    &amp;lt;alter&amp;gt;-1&amp;lt;/alter&amp;gt;
                    &amp;lt;octave&amp;gt;4&amp;lt;/octave&amp;gt;
                  &amp;lt;/pitch&amp;gt;
                  &amp;lt;duration&amp;gt;1&amp;lt;/duration&amp;gt;
                  &amp;lt;type&amp;gt;16th&amp;lt;/type&amp;gt;
                  &amp;lt;stem&amp;gt;up&amp;lt;/stem&amp;gt;
                  &amp;lt;lyric&amp;gt;
                    &amp;lt;syllabic&amp;gt;single&amp;lt;/syllabic&amp;gt;
                    &amp;lt;text&amp;gt;nicht,&amp;lt;/text&amp;gt;
                  &amp;lt;/lyric&amp;gt;
                &amp;lt;/note&amp;gt;
                &amp;lt;note&amp;gt;
                  &amp;lt;pitch&amp;gt;
                    &amp;lt;step&amp;gt;E&amp;lt;/step&amp;gt;
                    &amp;lt;alter&amp;gt;-1&amp;lt;/alter&amp;gt;
                    &amp;lt;octave&amp;gt;4&amp;lt;/octave&amp;gt;
                  &amp;lt;/pitch&amp;gt;
                  &amp;lt;duration&amp;gt;2&amp;lt;/duration&amp;gt;
                  &amp;lt;type&amp;gt;eighth&amp;lt;/type&amp;gt;
                  &amp;lt;stem&amp;gt;up&amp;lt;/stem&amp;gt;
                  &amp;lt;lyric&amp;gt;
                    &amp;lt;syllabic&amp;gt;begin&amp;lt;/syllabic&amp;gt;
                    &amp;lt;text&amp;gt;heil&amp;lt;/text&amp;gt;
                  &amp;lt;/lyric&amp;gt;
                &amp;lt;/note&amp;gt;
                &amp;lt;note&amp;gt;
                  &amp;lt;pitch&amp;gt;
                    &amp;lt;step&amp;gt;B&amp;lt;/step&amp;gt;
                    &amp;lt;alter&amp;gt;-1&amp;lt;/alter&amp;gt;
                    &amp;lt;octave&amp;gt;4&amp;lt;/octave&amp;gt;
                  &amp;lt;/pitch&amp;gt;
                  &amp;lt;duration&amp;gt;1&amp;lt;/duration&amp;gt;
                  &amp;lt;type&amp;gt;16th&amp;lt;/type&amp;gt;
                  &amp;lt;stem&amp;gt;up&amp;lt;/stem&amp;gt;
                  &amp;lt;beam number=&amp;quot;1&amp;quot;&amp;gt;begin&amp;lt;/beam&amp;gt;
                  &amp;lt;beam number=&amp;quot;2&amp;quot;&amp;gt;begin&amp;lt;/beam&amp;gt;
                  &amp;lt;notations&amp;gt;
                    &amp;lt;slur type=&amp;quot;start&amp;quot; number=&amp;quot;1&amp;quot;/&amp;gt;
                  &amp;lt;/notations&amp;gt;
                  &amp;lt;lyric&amp;gt;
                    &amp;lt;syllabic&amp;gt;end&amp;lt;/syllabic&amp;gt;
                    &amp;lt;text&amp;gt;ger&amp;lt;/text&amp;gt;
                    &amp;lt;extend/&amp;gt;
                  &amp;lt;/lyric&amp;gt;
                &amp;lt;/note&amp;gt;
                &amp;lt;note&amp;gt;
                  &amp;lt;pitch&amp;gt;
                    &amp;lt;step&amp;gt;G&amp;lt;/step&amp;gt;
                    &amp;lt;octave&amp;gt;4&amp;lt;/octave&amp;gt;
                  &amp;lt;/pitch&amp;gt;
                  &amp;lt;duration&amp;gt;1&amp;lt;/duration&amp;gt;
                  &amp;lt;type&amp;gt;16th&amp;lt;/type&amp;gt;
                  &amp;lt;stem&amp;gt;up&amp;lt;/stem&amp;gt;
                  &amp;lt;beam number=&amp;quot;1&amp;quot;&amp;gt;end&amp;lt;/beam&amp;gt;
                  &amp;lt;beam number=&amp;quot;2&amp;quot;&amp;gt;end&amp;lt;/beam&amp;gt;
                  &amp;lt;notations&amp;gt;
                    &amp;lt;slur type=&amp;quot;stop&amp;quot; number=&amp;quot;1&amp;quot;/&amp;gt;
                  &amp;lt;/notations&amp;gt;
                  &amp;lt;lyric&amp;gt;
                    &amp;lt;extend/&amp;gt;
                  &amp;lt;/lyric&amp;gt;
                &amp;lt;/note&amp;gt;
              &amp;lt;/measure&amp;gt;
            &amp;lt;/part&amp;gt;
          &amp;lt;/score-partwise&amp;gt;
      &amp;lt;/script&amp;gt;
    &amp;lt;/sheet-music&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are passing a src attribute to the web component for this example but we can also add a script tag with the type attribute set to &lt;code&gt;text/xml&lt;/code&gt; with the contents containing the json.&lt;/p&gt;
&lt;h2&gt;Web Component&lt;/h2&gt;
&lt;p&gt;Before we update our component we need to rename &lt;code&gt;my-element.ts&lt;/code&gt; to &lt;code&gt;sheet-music.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Open up &lt;code&gt;sheet-music.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html, css, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, property, query } from &amp;quot;lit/decorators.js&amp;quot;;
import { IOSMDOptions, OpenSheetMusicDisplay } from &amp;quot;opensheetmusicdisplay&amp;quot;;

type BackendType = &amp;quot;svg&amp;quot; | &amp;quot;canvas&amp;quot;;
type DrawingType = &amp;quot;compact&amp;quot; | &amp;quot;default&amp;quot;;

@customElement(&amp;quot;sheet-music&amp;quot;)
export class SheetMusic extends LitElement {
  _zoom = 1.0;

  @property({ type: Boolean }) allowDrop = false;
  @property() src = &amp;quot;&amp;quot;;

  @query(&amp;quot;main&amp;quot;) canvas!: HTMLElement;

  controller?: OpenSheetMusicDisplay;
  options: IOSMDOptions = {
    autoResize: true,
    backend: &amp;quot;canvas&amp;quot; as BackendType,
    drawingParameters: &amp;quot;default&amp;quot; as DrawingType,
  };

  static styles = css`
    main {
      overflow-x: auto;
    }
  `;

  render() {
    return html`&amp;lt;main&amp;gt;&amp;lt;/main&amp;gt;`;
  }

  async renderMusic(content: string) {
    if (!this.controller) return;
    await this.controller.load(content);
    this.controller.zoom = this._zoom;
    this.controller.render();
    this.requestUpdate();
  }

  private async getMusic(): Promise&amp;lt;string&amp;gt; {
    // Check if src attribute is set and prefer it over the slot
    if (this.src.length &amp;gt; 0) return fetch(this.src).then((res) =&amp;gt; res.text());

    // Check if slot children exist and return the xml
    const elem = this.parentElement?.querySelector(
      &apos;script[type=&amp;quot;text/xml&amp;quot;]&apos;
    ) as HTMLScriptElement;
    if (elem) return elem.innerHTML;

    // Return nothing if neither is found
    return &amp;quot;&amp;quot;;
  }

  async firstUpdated() {
    this.controller = new OpenSheetMusicDisplay(this.canvas, this.options);
    this.requestUpdate();

    // Check for any music and update if found
    const music = await this.getMusic();
    if (music.length &amp;gt; 0) this.renderMusic(music);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    &amp;quot;sheet-music&amp;quot;: SheetMusic;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;npm run dev&lt;/code&gt; and the following should appear if all went well:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/s_1_4wd2mls47h.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to learn more about building with Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source for this example can be found &lt;a href=&quot;https://github.com/rodydavis/lit-sheet-music&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/s_2_mkoqbtpadj.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</content:encoded></item><item><title>How to store SQLite as NoSQL Store</title><link>https://rodydavis.com/sqlite/no-sql/</link><guid isPermaLink="true">https://rodydavis.com/sqlite/no-sql/</guid><description>SQLite can function as a NoSQL store by utilizing its JSON support (including JSONB) to store schema-less JSON documents in a structured table.</description><pubDate>Sat, 18 Jan 2025 06:24:22 GMT</pubDate><content:encoded>&lt;h1&gt;How to store SQLite as NoSQL Store&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt; is a very capable edge database that can store various shapes of data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mongodb.com/nosql-explained#:~:text=Some%20say%20the%20term%20%E2%80%9CNoSQL,format%20other%20than%20relational%20tables.&quot;&gt;NoSQL databases&lt;/a&gt; are very popular due to the schema-less nature of storing of the data but it is totally possible to store these documents in SQLite.&lt;/p&gt;
&lt;p&gt;SQLite actually has great &lt;a href=&quot;https://www.sqlite.org/json1.html&quot;&gt;JSON support&lt;/a&gt; and even supports &lt;a href=&quot;https://sqlite.org/draft/jsonb.html&quot;&gt;JSONB&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Create the table&lt;/h2&gt;
&lt;p&gt;To store JSON documents we need to create a table to store the values as strings.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE documents (
  path TEXT NOT NULL PRIMARY KEY,
  data TEXT,
  ttl INTEGER,
  created INTEGER NOT NULL,
  updated INTEGER NOT NULL,
  UNIQUE(path)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;path&lt;/th&gt;
&lt;th&gt;data&lt;/th&gt;
&lt;th&gt;ttl&lt;/th&gt;
&lt;th&gt;created&lt;/th&gt;
&lt;th&gt;updated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/posts/1&lt;/td&gt;
&lt;td&gt;{&amp;quot;id&amp;quot;:1}&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/posts/2&lt;/td&gt;
&lt;td&gt;{&amp;quot;id&amp;quot;:2}&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/users/1&lt;/td&gt;
&lt;td&gt;{&amp;quot;id&amp;quot;:1}&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The basic idea is to store a JSON object and an unique path.&lt;/p&gt;
&lt;p&gt;There is an optional &lt;a href=&quot;https://www.cloudflare.com/learning/cdn/glossary/time-to-live-ttl/#:~:text=What%20is%20time%2Dto%2Dlive%20(TTL)%20in%20networking,CDN%20caching%20and%20DNS%20caching.&quot;&gt;TTL&lt;/a&gt; to automatically delete rows when they reach the stale date.&lt;/p&gt;
&lt;h2&gt;Save a document&lt;/h2&gt;
&lt;p&gt;To save a document we can encode our JSON as a string or binary and save in in the table with a unique path.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT OR REPLACE 
INTO documents (path, data, ttl, created, updated) 
VALUES (:path, :data, :ttl, :created, :updated)
RETURNING *;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use JSON functions to save the Object to a valid JSON string.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT OR REPLACE 
INTO documents (path, data, ttl, created, updated) 
VALUES (&amp;quot;/posts/1&amp;quot;, json(&apos;{&amp;quot;id&amp;quot; 1}&apos;), NULL, 0, 0)
RETURNING *;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;path&lt;/th&gt;
&lt;th&gt;data&lt;/th&gt;
&lt;th&gt;ttl&lt;/th&gt;
&lt;th&gt;created&lt;/th&gt;
&lt;th&gt;updated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/posts/1&lt;/td&gt;
&lt;td&gt;{&amp;quot;id&amp;quot;:1}&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Reading a document&lt;/h2&gt;
&lt;p&gt;To read a document we just need the path. If a TTL is set we can &lt;a href=&quot;https://www.sqlite.org/lang_datefunc.html&quot;&gt;calculate if the current date&lt;/a&gt; is greater than the offset and not return the document.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT * FROM documents 
WHERE path = :path
AND (
	(ttl IS NOT NULL AND ttl + updated &amp;lt; unixepoch())
	OR
	ttl IS NULL
);
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;path&lt;/th&gt;
&lt;th&gt;data&lt;/th&gt;
&lt;th&gt;ttl&lt;/th&gt;
&lt;th&gt;created&lt;/th&gt;
&lt;th&gt;updated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/posts/1&lt;/td&gt;
&lt;td&gt;{&amp;quot;id&amp;quot;:1}&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Get documents for a collection&lt;/h2&gt;
&lt;p&gt;We can query all the docs for a given collection using some built-in functions and a path prefix:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT *
FROM documents 
WHERE (
	path LIKE :prefix
	AND
	(LENGTH(path) - LENGTH(REPLACE(path, &apos;/&apos;, &apos;&apos;))) = (LENGTH(:prefix) - LENGTH(REPLACE(:prefix, &apos;/&apos;, &apos;&apos;)))
)
AND (
	(ttl IS NOT NULL AND ttl + updated &amp;lt; unixepoch())
	OR
	ttl IS NULL
)
ORDER BY created;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is expected to search for a :prefix with the &lt;code&gt;/%&lt;/code&gt; at the end:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;quot;/my/path/%&amp;quot; // search for /my/path&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Deleting expired documents&lt;/h2&gt;
&lt;p&gt;Using the TTL field we can delete all expired documents:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;DELETE FROM documents
WHERE ttl IS NOT NULL
AND ttl + updated &amp;lt; unixepoch();
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>SQLite on the UI Thread</title><link>https://rodydavis.com/sqlite/ui-thread/</link><guid isPermaLink="true">https://rodydavis.com/sqlite/ui-thread/</guid><description>SQLite, when used correctly for UI-driven applications, can provide extremely fast and seamless data access, often outperforming asynchronous approaches and enabling high performance even with massive datasets.</description><pubDate>Sat, 18 Jan 2025 20:38:57 GMT</pubDate><content:encoded>&lt;h1&gt;SQLite on the UI Thread&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt; is a lot faster than you may realize. In &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; for example there is &lt;a href=&quot;https://pub.dev/packages/drift&quot;&gt;drift&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/sqlite_async&quot;&gt;sqlite_async&lt;/a&gt; and &lt;a href=&quot;https://pub.dev/packages/sqflite&quot;&gt;sqflite&lt;/a&gt; which allow for async access of data. But with &lt;a href=&quot;https://pub.dev/packages/sqlite3&quot;&gt;sqlite3&lt;/a&gt; you can query with sync functions! 🤯&lt;/p&gt;
&lt;p&gt;Here is a list view where there are 10000 items and each item is retrieved with a select statement 👀&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/demo_tobl81yuqz.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://gist.github.com/rodydavis/4a6dca4a2e1afc530ac93e94a76a594a&quot;&gt;https://gist.github.com/rodydavis/4a6dca4a2e1afc530ac93e94a76a594a&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SQLite, when used effectively, can be a powerful asset for UI-driven applications. Operating within the same process and thread as the UI, it offers a seamless integration that can significantly improve component building.&lt;/p&gt;
&lt;p&gt;Async/await does not mean you will be building the most performant applications, and in some cases will &lt;a href=&quot;https://madelinemiller.dev/blog/javascript-promise-overhead/&quot;&gt;incur a performance penalty&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Even with extensive datasets, SQLite demonstrates remarkable efficiency. Its ability to handle millions of rows without compromising speed is a testament to its robust architecture. Contrary to the misconception of being solely a background-thread database, SQLite functions as a process-level library, akin to any other C-based library.&lt;/p&gt;
&lt;p&gt;By strategically employing indexes and queries, developers can achieve nanosecond response times and mitigate N+1 query issues. The judicious use of views, indexes, and virtual tables is paramount in optimizing performance.&lt;/p&gt;
&lt;p&gt;Complex join operations and the ability to retrieve only essential data for display further underscore SQLite&apos;s versatility. For example, when presenting a list view or cards, SQLite can efficiently fetch the required 30 items without undue overhead.&lt;/p&gt;
&lt;p&gt;SQLite&apos;s flexibility extends beyond single-database scenarios. The &lt;a href=&quot;https://www.sqlite.org/lang_attach.html&quot;&gt;ATTACH&lt;/a&gt; feature enables the management of multiple databases within a single application. Additionally, the concept of isolates or workers allows for parallel processing, further enhancing performance and responsiveness.&lt;/p&gt;
&lt;p&gt;From simple &lt;a href=&quot;https://rodydavis.com/sqlite/key-value&quot;&gt;key-value&lt;/a&gt; stores to intricate data modeling, SQLite&apos;s capabilities are vast. By applying appropriate &lt;a href=&quot;https://www.sqlite.org/pragma.html&quot;&gt;PRAGMAs&lt;/a&gt;, such as WAL mode, developers can tailor SQLite&apos;s behavior to meet specific application requirements.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/rails/comments/16cbiz9/the_6_pragmas_you_need_to_know_to_tune_your/&quot;&gt;Example PRAGMA&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA journal_size_limit = 67108864;
PRAGMA mmap_size = 134217728;
PRAGMA cache_size = 2000;
PRAGMA busy_timeout = 5000;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Narobi National Park</title><link>https://rodydavis.com/travel/narobi-national-park/</link><guid isPermaLink="true">https://rodydavis.com/travel/narobi-national-park/</guid><description>Narobi National Park is a unique Nairobi attraction offering a harmonious blend of city life and wildlife, allowing visitors to spot diverse animals like rhinos, giraffes, and hippos against an urban backdrop.</description><pubDate>Sun, 19 Jan 2025 03:23:49 GMT</pubDate><content:encoded>&lt;h1&gt;Narobi National Park&lt;/h1&gt;
&lt;p&gt;On a recent &lt;a href=&quot;https://flutter.dev/events/flutter-forward&quot;&gt;work trip&lt;/a&gt; to Nairobi, Kenya I was able to take a day trip to &lt;a href=&quot;http://www.kws.go.ke/parks/nairobi-national-park&quot;&gt;Narobi National Park&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What is so wild about this national park is the city is 100% visible from the part at all times. You can grab a pic of a Rhino with a sky scraper in the background. There is a sense of harmony and respect I really love about this place.&lt;/p&gt;
&lt;h2&gt;Rhinos&lt;/h2&gt;
&lt;p&gt;Something I have always wanted to see is &lt;a href=&quot;https://www.worldwildlife.org/species/white-rhino&quot;&gt;White Rhinos&lt;/a&gt;. On a trip to &lt;a href=&quot;https://www.krugerpark.co.za/&quot;&gt;Kruger National Park&lt;/a&gt; in South Africa I was not able to see them because of how protected they are. They would explicitly not track them on the game boards and had men with guns to protect them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/n_1_9ekddhd4j3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In addition to the adults there were also babies. They were sleeps so peacefully. I was able to get a few shots of them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/n_2_jduubmzdxj.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Giraffes&lt;/h2&gt;
&lt;p&gt;Giraffes are an animal you probably get to see a lot in the zoo, but when seeing it in the wild it still is incredible. They travel together and you can pick them out from the trees. They are so tall and the adults can get up to 18 feet tall.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/n_3_dg8gghux4x.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Later that day we got to see the giraffes at the &lt;a href=&quot;https://www.giraffecentre.org/&quot;&gt;Giraffe Center&lt;/a&gt;. It was a really cool experience. You can feed them from a platform and they will come up and eat from your hand. It was a really cool experience. The tongue is so long and feels like sand paper.&lt;/p&gt;
&lt;h2&gt;Hippos&lt;/h2&gt;
&lt;p&gt;Hippos are so powerful and can be very dangerous. It&apos;s easy to forget that they are animals and not just statues. While they all lay in the water together, they are still very territorial and will attack if you get too close.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/h_4_exp3tn4or8.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Impalas&lt;/h2&gt;
&lt;p&gt;The impalas are so cute and are the most common animal in the park. They are very fast and can jump up to 10 feet in the air.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/h_5_ugjq51azle.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Water Buffalo&lt;/h2&gt;
&lt;p&gt;The water buffalo are so cool. They are so big and can weigh up to 2,000 pounds. We got to see many skulls and feel the horns which are so dense.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/h_6_vrdg7nb4hy.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Zebras&lt;/h2&gt;
&lt;p&gt;Zebras are something that is also common to see in zoos but in the wild they are so cool. We saw so many of them in the park and quite a few babies.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/n_7_4pac9s40u1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Location&lt;/h2&gt;
</content:encoded></item><item><title>The Perfect Brisket</title><link>https://rodydavis.com/recipes/the-perfect-brisket/</link><guid isPermaLink="true">https://rodydavis.com/recipes/the-perfect-brisket/</guid><description>A detailed guide on preparing and smoking a perfect 13-16lb beef brisket using specific ingredients, tools, and preparation steps.</description><pubDate>Sun, 19 Jan 2025 02:57:22 GMT</pubDate><content:encoded>&lt;h1&gt;The Perfect Brisket&lt;/h1&gt;
&lt;p&gt;This is a living document meant to be updated each time I improve on the perfect smoked brisket recipe. I don&apos;t get to cook it often but when I do I go all out.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Be prepared to take 4-6hrs to smoke the meat&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Ingredients&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;13-16lb Whole Beef Brisket&lt;/li&gt;
&lt;li&gt;Classic Yellow Mustard&lt;/li&gt;
&lt;li&gt;Dry Rub (Mr. P&apos;s Rub-a-butt)&lt;/li&gt;
&lt;li&gt;Large Cutting Board&lt;/li&gt;
&lt;li&gt;Very Sharp Knife&lt;/li&gt;
&lt;li&gt;Smoker (Akorn)&lt;/li&gt;
&lt;li&gt;Apple Wood Chips&lt;/li&gt;
&lt;li&gt;Charcoal Briquettes&lt;/li&gt;
&lt;li&gt;Meat Thermometer&lt;/li&gt;
&lt;li&gt;Aluminum Foil&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_1_eso97ppzim.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;First you need to source the brisket. I usually like to go to a local butcher shop and look for some good deals. This last time I went it was at $6.99/lb but I have gotten it as low as $3.99/lb. To cook for a big family and have leftovers you get get a 13-16lb brisket (This is usually what they already have in stock).&lt;/p&gt;
&lt;h2&gt;Prepare The Meat&lt;/h2&gt;
&lt;p&gt;After you have the meat out of the packaging you can place it on a cutting board and start to pat dry the meat. This will take quite a few paper towels to dry all the blood and moisture.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_2_73r2h4ft7u.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;After the meat is dry you can grab a very sharp curved knife to be used to trim the fat. The goal of the trimming is to remove most of the hard fat in the thick areas.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_3_e7wg250gea.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You shouldn&apos;t have to trim that much unless you are preparing for a competition but in this case I am preparing for a group of people and need more meat. As you are trimming the fat you want to make sure to only cut the thick hard parts and not into the meat (it&apos;s ok if this is not perfect, you can use the trimmings for other stuff).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_4_lkvv57zpw1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Season The Meat&lt;/h2&gt;
&lt;p&gt;With the meat trimmed it is now time to put the dry rub on the brisket. You will need to grab the mustard and whichever seasoning you chose. Be prepared to get very messy at this step, and id you have a helping hand that would be even better (Gloves also work).&lt;/p&gt;
&lt;p&gt;Start of with lines of mustard on the meat so that you have a nice even coat to rub in. Don&apos;t worry if you do not like mustard like myself, it will cook out and is only used to keep the seasoning on the meat.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_5_jol8llekzu.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now add the seasoning and rub it in with your hands or barbecue brush and repeat on the opposite side. You want to get a nice thick coat.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_6_n1a3i4ibsw.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;After you finish the meat make sure you leave it out for 20 min to bring it up to room temperature while you prepare the grill.&lt;/p&gt;
&lt;h2&gt;Prepare The Smoker&lt;/h2&gt;
&lt;p&gt;While the prepared meat is sitting you will need to prepare the grill. You need to light the coals but not all of them. I like to like the center of them and then control the airflow. Keep in mind you will need to keep the smoker under 250F throughout the duration of the cook.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_7_33rbpf4lhk.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Another thing I like to use is a diffuser to provide indirect heat and allow just the smoke to cook it. If you do not have this it is not a problem. You can also add the apple wood chips inside a aluminum pouch at this time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_8_nk5r86f3ky.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This may take about 20-30 min to bring the grill up to the right temperature before putting the meat on.&lt;/p&gt;
&lt;h2&gt;Cook the Meat&lt;/h2&gt;
&lt;p&gt;Now that the meat is room temperature and the grill is warm or close to the right temperature, grab the meat and place it on the smoker.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_9_r4vpfo1tjd.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once you have placed the meat on the grate you can put in the temperature sensor sideways in the thickest side of the meat.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_10_9ynpsv0gg1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now comes the fun part... waiting. You will need to come out every 30-60 min to make sure the meat is not getting too hot/cold and make micro adjustments to the vents.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_11_pu24vfrh4u.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The internal temperature you are shooting for is 190F and it will take awhile for it to come up. When the meat is at 150F or it&apos;s been 3 hours and the skin is getting brown, you can wrap the meat in foil to lock in the juices.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_12_6bc97byurs.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The foil should cover all sides of the meat, and around this time you can add more wood chips to increase the smoke.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_13_6qxv0y27nt.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Enjoy&lt;/h2&gt;
&lt;p&gt;Regardless of how it turned out it should still smell amazing and taste even better. If you made sure to come check on it often and not let it get to hot it should still be nice and juicy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/brisket_14_exz9yiawi9.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It will be really tempting to eat it right away, but you need to let the meat rest for 20 min before eating.&lt;/p&gt;
&lt;p&gt;Let me know how it turned out by mentioning me on &lt;a href=&quot;https://twitter.com/rodydavis&quot;&gt;twitter&lt;/a&gt; or &lt;a href=&quot;https://instagram.com/rodydavisjr&quot;&gt;instagram&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>File Based Routing for Static Sites</title><link>https://rodydavis.com/web/file-based-routing/</link><guid isPermaLink="true">https://rodydavis.com/web/file-based-routing/</guid><description>Learn how to build a multi-page static site application using file-based routing techniques.</description><pubDate>Sun, 19 Jan 2025 23:59:39 GMT</pubDate><content:encoded>&lt;h1&gt;File Based Routing for Static Sites&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to use file based routing to output as a static site multi page application.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/static-site-file-based-routing&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/static-site-file-based-routing/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Step 1&lt;/h2&gt;
&lt;p&gt;Create a new folder called “static-site-file-based-routing” and open it up in VSCode.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;mkdir static-site-file-based-routing
cd static-site-file-based-routing
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2&lt;/h2&gt;
&lt;p&gt;Create a &lt;code&gt;tsconfig.json&lt;/code&gt; and replace it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &amp;quot;compilerOptions&amp;quot;: {
    &amp;quot;incremental&amp;quot;: true,
    &amp;quot;target&amp;quot;: &amp;quot;es5&amp;quot;,
    &amp;quot;module&amp;quot;: &amp;quot;es2020&amp;quot;,
    &amp;quot;outDir&amp;quot;: &amp;quot;dist&amp;quot;,
    &amp;quot;strict&amp;quot;: true,
    &amp;quot;moduleResolution&amp;quot;: &amp;quot;node&amp;quot;,
    &amp;quot;esModuleInterop&amp;quot;: true,
    &amp;quot;skipLibCheck&amp;quot;: true,
    &amp;quot;forceConsistentCasingInFileNames&amp;quot;: true,
    &amp;quot;typeRoots&amp;quot;: [
      &amp;quot;node_modules/@types&amp;quot;,
      &amp;quot;src/@types&amp;quot;
    ]
  },
  &amp;quot;include&amp;quot;: [
    &amp;quot;src/**/*&amp;quot;
  ],
  &amp;quot;exclude&amp;quot;: [
    &amp;quot;node_modules&amp;quot;,
    &amp;quot;dist&amp;quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 3&lt;/h2&gt;
&lt;p&gt;Create a &lt;code&gt;package.json&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &amp;quot;name&amp;quot;: &amp;quot;static-site-file-based-routing&amp;quot;,
  &amp;quot;version&amp;quot;: &amp;quot;1.0.0&amp;quot;,
  &amp;quot;description&amp;quot;: &amp;quot;File based routing for HTML MPA&amp;quot;,
  &amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,
  &amp;quot;scripts&amp;quot;: {
    &amp;quot;start&amp;quot;: &amp;quot;wds --node-resolve --root-dir build --base-path /static-site-file-based-routing --open --watch&amp;quot;,
    &amp;quot;postinstall&amp;quot;: &amp;quot;npm run tsc&amp;quot;,
    &amp;quot;build&amp;quot;: &amp;quot;node dist/main.js --inputDir ./example --outputDir ./build&amp;quot;,
    &amp;quot;dev&amp;quot;: &amp;quot;node dist/main.js --inputDir ./example --outputDir ./build -w&amp;quot;,
    &amp;quot;tsc&amp;quot;: &amp;quot;tsc&amp;quot;,
    &amp;quot;tsc:watch&amp;quot;: &amp;quot;tsc -w&amp;quot;
  },
  &amp;quot;devDependencies&amp;quot;: {
    &amp;quot;@pkgjs/parseargs&amp;quot;: &amp;quot;^0.10.0&amp;quot;,
    &amp;quot;@types/markdown-it&amp;quot;: &amp;quot;^12.2.3&amp;quot;,
    &amp;quot;@types/node&amp;quot;: &amp;quot;^18.7.23&amp;quot;,
    &amp;quot;@web/dev-server&amp;quot;: &amp;quot;^0.1.34&amp;quot;,
    &amp;quot;chokidar&amp;quot;: &amp;quot;^3.5.3&amp;quot;,
    &amp;quot;highlight.js&amp;quot;: &amp;quot;^11.6.0&amp;quot;,
    &amp;quot;html-format&amp;quot;: &amp;quot;^1.0.2&amp;quot;,
    &amp;quot;markdown-it&amp;quot;: &amp;quot;^13.0.1&amp;quot;,
    &amp;quot;parse5&amp;quot;: &amp;quot;^7.1.1&amp;quot;,
    &amp;quot;typescript&amp;quot;: &amp;quot;^4.8.4&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run &lt;code&gt;npm install&lt;/code&gt; to install all the dependencies.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I am using &lt;code&gt;@web/dev-server&lt;/code&gt; to serve the site locally. You can use any server you want.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These dependencies are used for various file transformations such as markdown to HTML, HTML formatting, and file watching.&lt;/p&gt;
&lt;h2&gt;Step 4&lt;/h2&gt;
&lt;p&gt;Create a &lt;code&gt;src&lt;/code&gt; folder and add 4 files:&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;src/build.ts&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import * as fs from &amp;quot;fs&amp;quot;;
import { compileDir, compileTarget } from &amp;quot;./compile.js&amp;quot;;
import * as path from &amp;quot;path&amp;quot;;
import chokidar from &amp;quot;chokidar&amp;quot;;
import { publicDirectory } from &amp;quot;./static.js&amp;quot;;

interface Options {
    inputDir?: string;
    outputDir?: string;
    publicDir?: string;
    watch?: boolean;
    clean?: boolean;
}

export default async function build(options: Options) {
    const inputDir = options.inputDir || &amp;quot;www&amp;quot;;
    const outputDir = options.outputDir || &amp;quot;build&amp;quot;;
    const publicDir = options.publicDir || &amp;quot;public&amp;quot;;
    const watch = options.watch || false;
    const clean = options.clean || false;

    if (!fs.existsSync(inputDir)) {
        throw new Error(`Input directory ${inputDir} does not exist`);
    }

    if (clean) {
        if (fs.existsSync(outputDir)) {
            fs.rmdirSync(outputDir, { recursive: true });
        }
    }

    if (!fs.existsSync(outputDir)) {
        fs.mkdirSync(outputDir, { recursive: true });
    }

    if (watch) {
        console.log(&amp;quot;Watching for changes...&amp;quot;);
        chokidar.watch(inputDir).on(&amp;quot;all&amp;quot;, async (event, inputFile) =&amp;gt; {
            console.log(event, inputFile);
            if (fs.existsSync(inputFile)) {
                const relativePath = path.relative(inputDir, inputFile);
                const outputFile = `${outputDir}/${relativePath}`;

                const stat = fs.statSync(inputFile);
                if (stat.isDirectory()) {
                    await compileDir(inputFile, outputFile);
                } else if (stat.isFile()) {
                    const filename = path.basename(inputFile);
                    if (filename === &amp;quot;layout.html&amp;quot;) {
                        // Rebuild all related directories
                        const dir = path.dirname(inputFile);
                        const inDir = path.relative(inputDir, dir);
                        const outDir = `${outputDir}/${inDir}`;
                        await compileDir(dir, outDir);
                    } else {
                        await compileTarget(inputFile, outputFile);
                    }
                }
            }
        });
    } else {
        await compileDir(inputDir, outputDir);
    }

    if (publicDir.split(&apos;,&apos;).length &amp;gt; 1) {
        for (const dir of publicDir.split(&apos;,&apos;)) {
            publicDirectory(dir, outputDir, watch);
        }
    } else {
        publicDirectory(publicDir, outputDir, watch);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;src/compile.ts&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import * as fs from &amp;quot;fs&amp;quot;;
import * as path from &amp;quot;path&amp;quot;;
import MarkdownIt from &amp;quot;markdown-it&amp;quot;;
import hljs from &amp;quot;highlight.js&amp;quot;;
import * as parse5 from &amp;quot;parse5&amp;quot;;
import type { Document } from &amp;quot;parse5/dist/tree-adapters/default.js&amp;quot;;
import format from &apos;html-format&apos;;

function compile(file: string) {
    const raw = fs.readFileSync(file, &amp;quot;utf-8&amp;quot;);
    const ext = path.extname(file);

    switch (ext) {
        case &amp;quot;.md&amp;quot;:
        case &amp;quot;.markdown&amp;quot;:
            const md = new MarkdownIt({
                html: true,
                linkify: true,
                typographer: true,
                highlight: function (str, lang) {
                    if (lang &amp;amp;&amp;amp; hljs.getLanguage(lang)) {
                        try {
                            return (
                                &apos;&amp;lt;pre class=&amp;quot;hljs&amp;quot;&amp;gt;&amp;lt;code&amp;gt;&apos; +
                                hljs.highlight(str, { language: lang, ignoreIllegals: true })
                                    .value +
                                &amp;quot;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;quot;
                            );
                        } catch (__) {
                            console.error(__);
                        }
                    }
                    return &amp;quot;&amp;quot;;
                },
            });
            return parse5.parse(md.render(raw));
        case &amp;quot;.html&amp;quot;:
            return parse5.parse(raw);
        default:
            break;
    }

    return raw;
}

function createHtml(options?: { head?: string; body?: string; }) {
    return `
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
${options?.head ?? &amp;quot;&amp;quot;}
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
${options?.body ?? &amp;quot;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;quot;}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
`;
}

export async function compileFile(file: string, target: string) {
    // Use regex to check if ends with index.*
    const isIndex = /index\.([a-z]+)$/i.test(file);

    if (!isIndex &amp;amp;&amp;amp; !fs.statSync(file).isDirectory()) {
        // Skipping for nested layouts
        return;
    }

    const parent = path.dirname(target);

    // Render up the directory tree until we hit the root directory
    const files: string[] = [file];
    let filePath = file;
    while (filePath !== parent) {
        filePath = path.dirname(filePath);
        // Check for root level layout
        const layout = path.join(filePath, &amp;quot;layout.html&amp;quot;);
        if (fs.existsSync(layout)) {
            files.unshift(layout);
        }
        if (filePath === &amp;quot;.&amp;quot;) break;
    }

    // Check for root level index markdown or html
    const layout = path.join(parent, &amp;quot;layout.html&amp;quot;);
    if (fs.existsSync(layout)) {
        if (
            fs.existsSync(path.join(parent, &amp;quot;index.html&amp;quot;)) ||
            fs.existsSync(path.join(parent, &amp;quot;index.md&amp;quot;)) ||
            fs.existsSync(path.join(parent, &amp;quot;index.markdown&amp;quot;))
        ) {
            files.unshift(layout);
        }
    }

    let output = createHtml();

    for (const item of files) {
        const doc = compile(item);
        if (typeof doc !== &apos;string&apos;) {
            const content = parse5.serialize(doc);
            output = mergeDocuments(output, content);
        }
    }

    // Replace extension
    const ext = path.extname(file);
    const newFile = target.replace(ext, &apos;.html&apos;);

    // Check if parent directory exists
    const parentDir = path.dirname(newFile);
    if (!fs.existsSync(parentDir)) {
        fs.mkdirSync(parentDir, { recursive: true });
    }

    fs.writeFileSync(newFile, output);
    console.log(`--&amp;gt; ${newFile}`);
}

function mergeDocuments(current: string, source: string) {
    let raw = current;

    // Merge body
    const html = extractDoc(parse5.parse(source));
    // Check for &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
    const hasSlot = raw.includes(&amp;quot;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;quot;);
    if (hasSlot) {
        raw = raw.replace(&amp;quot;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;quot;, parse5.serialize(html.body));
    } else {
        // Append to body
        const endBodyIdx = raw.lastIndexOf(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
        const start = raw.slice(0, endBodyIdx);
        const end = raw.slice(endBodyIdx);
        const body = parse5.serialize(html.body);
        raw = start + body + end;
    }

    // Merge head
    const endHeadIdx = raw.lastIndexOf(&amp;quot;&amp;lt;/head&amp;gt;&amp;quot;);
    const start = raw.slice(0, endHeadIdx);
    const end = raw.slice(endHeadIdx);
    const head = parse5.serialize(html.head);
    raw = start + head + end;

    // Format
    raw = format(raw);

    // Remove duplicate title tags
    const lastTitle = raw.lastIndexOf(&amp;quot;&amp;lt;title&amp;gt;&amp;quot;);
    const lastTitleEnd = raw.lastIndexOf(&amp;quot;&amp;lt;/title&amp;gt;&amp;quot;);
    const title = raw.slice(lastTitle, lastTitleEnd + 8);
    raw = raw.replace(/&amp;lt;title&amp;gt;.*&amp;lt;\/title&amp;gt;/, &amp;quot;&amp;quot;);
    raw = raw.replace(&amp;quot;&amp;lt;/head&amp;gt;&amp;quot;, title + &amp;quot;&amp;lt;/head&amp;gt;&amp;quot;);

    return raw;
}

function extractDoc(doc: Document) {
    const html = (doc.childNodes[1] ?? doc.childNodes[0]) as unknown as Document;
    const head = html.childNodes.find(
        (node) =&amp;gt; node.nodeName === &amp;quot;head&amp;quot;
    ) as unknown as Document;
    const body = html.childNodes.find(
        (node) =&amp;gt; node.nodeName === &amp;quot;body&amp;quot;
    ) as unknown as Document;
    return { head, body };
}

export async function compileDir(inputDir: string, outputDir: string) {
    const files = fs.readdirSync(inputDir);
    for (const file of files) {
        const inputFile = `${inputDir}/${file}`;
        const outputFile = `${outputDir}/${file}`;

        await compileTarget(inputFile, outputFile);
    }
}

export async function compileTarget(input: string, output: string) {
    const stat = fs.statSync(input);
    if (stat.isDirectory()) {
        if (!fs.existsSync(output)) {
            fs.mkdirSync(output, { recursive: true });
        }
        await compileDir(input, output);
    } else if (stat.isFile()) {
        const ext = path.extname(input);
        if ([&apos;.html&apos;, &apos;.md&apos;, &apos;.markdown&apos;].includes(ext)) {
            await compileFile(input, output);
        } else {
            const current = fs.readFileSync(input);
            const parentDir = path.dirname(output);
            if (!fs.existsSync(parentDir)) {
                fs.mkdirSync(parentDir, { recursive: true });
            }
            if (fs.existsSync(output)) {
                // Check if content is the same
                const previous = fs.readFileSync(output);
                if (Buffer.compare(current, previous) !== 0) {
                    fs.writeFileSync(output, current);
                }
            } else {
                // Copy the file
                fs.copyFileSync(input, output);
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;src/static.ts&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import * as fs from &amp;quot;fs&amp;quot;;
import * as path from &amp;quot;path&amp;quot;;
import chokidar from &amp;quot;chokidar&amp;quot;;

export function publicDirectory(publicDir: string, outputDir: string, watch: boolean) {
    if (watch) {
        if (fs.existsSync(publicDir)) {
            chokidar.watch(publicDir).on(&amp;quot;all&amp;quot;, (event, inputFile) =&amp;gt; {
                console.log(event, inputFile);
                if (fs.existsSync(inputFile)) {
                    const relativePath = path.relative(publicDir, inputFile);
                    const outputFile = `${outputDir}/${relativePath}`;

                    const stat = fs.statSync(inputFile);
                    if (stat.isDirectory()) {
                        copyStaticFiles(inputFile, outputFile);
                    } else if (stat.isFile()) {
                        fs.copyFileSync(inputFile, outputFile);
                    }
                }
            });
        }
    } else {
        // Copy static files
        if (fs.existsSync(publicDir)) {
            copyStaticFiles(publicDir, outputDir);
        }
    }
}

function copyStaticFiles(inDir: string, outDir: string) {
    const files = fs.readdirSync(inDir);
    for (const file of files) {
        const inputFile = `${inDir}/${file}`;
        const outputFile = `${outDir}/${file}`;

        const stat = fs.statSync(inputFile);
        if (stat.isDirectory()) {
            if (!fs.existsSync(outputFile)) {
                fs.mkdirSync(outputFile, { recursive: true });
            }

            copyStaticFiles(inputFile, outputFile);
        } else if (stat.isFile()) {
            fs.copyFileSync(inputFile, outputFile);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;src/main.ts&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;#!/usr/bin/env node

// @ts-ignore
import { parseArgs } from &amp;quot;@pkgjs/parseargs&amp;quot;;
import build from &amp;quot;./build.js&amp;quot;;

export async function main() {
  const {
    values: { inputDir, outputDir, watch },
  } = parseArgs({
    options: {
      inputDir: {
        type: &amp;quot;string&amp;quot;,
        short: &amp;quot;i&amp;quot;,
      },
      outputDir: {
        type: &amp;quot;string&amp;quot;,
        short: &amp;quot;o&amp;quot;,
      },
      watch: {
        type: &amp;quot;boolean&amp;quot;,
        short: &amp;quot;w&amp;quot;,
      },
    },
    allowPositional: true,
  });

  if (inputDir === undefined || outputDir === undefined) {
    console.log(&amp;quot;Usage: build -i &amp;lt;inputDir&amp;gt; -o &amp;lt;outputDir&amp;gt; [-w]&amp;quot;);
    return;
  }

  await build({
    inputDir,
    outputDir,
    watch,
  });
}

main();

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 5&lt;/h2&gt;
&lt;p&gt;Now that the project is setup we can start the typescript compiler in watch mode.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run ts:watch
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 6&lt;/h2&gt;
&lt;p&gt;Now create a folder that will contain the source files for the website.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;example/index.md&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# Hello World

This is a test
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;example/style.css&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;body {
    background-color: #000;
    color: #fff;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;example/layout.html&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
    &amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot;&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;style.css&amp;quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 7&lt;/h2&gt;
&lt;p&gt;With the source files created we can now run the build script.&lt;/p&gt;
&lt;p&gt;For a one time build run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a watch mode run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now start the http server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;As you make changes it will only update affected files and be very fast to update.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that this does not bundle the javascript and will be up to you if you are using &lt;code&gt;node_modules&lt;/code&gt; in any files (for the example in the repo I show how to use &lt;strong&gt;UNPKG&lt;/strong&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you want to find the source code you can check it out &lt;a href=&quot;https://github.com/rodydavis/static-site-file-based-routing&quot;&gt;here&lt;/a&gt; otherwise thanks for reading and let me know if you have any questions!&lt;/p&gt;
</content:encoded></item><item><title>Building a HTML Element Sandbox with Lit</title><link>https://rodydavis.com/web/html-code-sandbox/</link><guid isPermaLink="true">https://rodydavis.com/web/html-code-sandbox/</guid><description>This guide details how to build a reusable HTML Element Sandbox component using Lit web components, enabling the development and live updating of embedded third-party content.</description><pubDate>Sun, 19 Jan 2025 06:17:00 GMT</pubDate><content:encoded>&lt;h1&gt;Building a HTML Element Sandbox with Lit&lt;/h1&gt;
&lt;p&gt;In this article I will go over how to set up a &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; web component and use it to create a HTML Element sandbox that can be used to update a live component.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; The final source &lt;a href=&quot;https://github.com/rodydavis/html-element-sandbox&quot;&gt;here&lt;/a&gt; and an online &lt;a href=&quot;https://rodydavis.github.io/html-element-sandbox/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vscode&lt;/li&gt;
&lt;li&gt;Node &amp;gt;= 16&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;We can start off by navigating in terminal to the location of the project and run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;npm init @vitejs/app --template lit-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then enter a project name &lt;code&gt;html-element-sandbox&lt;/code&gt; and now open the project in vscode and install the dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;cd html-element-sandbox
npm i lit
npm i -D @types/node
code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;vite.config.ts&lt;/code&gt; with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { defineConfig } from &amp;quot;vite&amp;quot;;
import { resolve } from &amp;quot;path&amp;quot;;

export default defineConfig({
  base: &amp;quot;/html-element-sandbox/&amp;quot;,
  build: {
    lib: {
      entry: &amp;quot;src/html-element-sandbox.ts&amp;quot;,
      formats: [&amp;quot;es&amp;quot;],
    },
    rollupOptions: {
      input: {
        main: resolve(__dirname, &amp;quot;index.html&amp;quot;),
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template&lt;/h2&gt;
&lt;p&gt;Open up the &lt;code&gt;index.html&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; href=&amp;quot;/src/favicon.svg&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;HTML Element Sandbox&amp;lt;/title&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;/src/html-element-sandbox.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      body {
        margin: 0;
        padding: 0;
        font-family: sans-serif;
      }
      html-element-sandbox {
        display: block;
        width: 100%;
        height: 100vh;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;html-element-sandbox&amp;gt;
      &amp;lt;template&amp;gt;
        &amp;lt;button
          class=&amp;quot;button&amp;quot;
          knob-text=&amp;quot;label&amp;quot;
          knob-css-color=&amp;quot;fg-color&amp;quot;
          knob-css-background-color=&amp;quot;bg-color&amp;quot;
          knob-css-border-radius=&amp;quot;shape&amp;quot;
          knob-css-font-size=&amp;quot;text-font-size&amp;quot;
          knob-css-padding=&amp;quot;padding&amp;quot;
          knob-css---shadow-color=&amp;quot;shadow&amp;quot;
        &amp;gt;
          My Button
        &amp;lt;/button&amp;gt;
        &amp;lt;style&amp;gt;
          .button {
            --shadow-color: #000;
            --elevation: 3px;
            display: block;
            width: 100%;
            height: 100%;
            border: none;
            background-color: transparent;
            cursor: pointer;
            box-shadow: 0 var(--elevation) calc(var(--elevation) * 2) 0 var(--shadow-color);
          }
        &amp;lt;/style&amp;gt;
      &amp;lt;/template&amp;gt;
      &amp;lt;div slot=&amp;quot;knobs&amp;quot;&amp;gt;
        &amp;lt;knob-string id=&amp;quot;label&amp;quot; name=&amp;quot;Label&amp;quot; value=&amp;quot;BUTTON&amp;quot;&amp;gt;&amp;lt;/knob-string&amp;gt;
        &amp;lt;knob-group name=&amp;quot;Style&amp;quot; expanded&amp;gt;
          &amp;lt;knob-color
            id=&amp;quot;bg-color&amp;quot;
            name=&amp;quot;Background Color&amp;quot;
            value=&amp;quot;#ff0000&amp;quot;
          &amp;gt;&amp;lt;/knob-color&amp;gt;
          &amp;lt;knob-color
            id=&amp;quot;fg-color&amp;quot;
            name=&amp;quot;Foreground Color&amp;quot;
            value=&amp;quot;#ffffff&amp;quot;
          &amp;gt;&amp;lt;/knob-color&amp;gt;
          &amp;lt;knob-color
            id=&amp;quot;shadow&amp;quot;
            name=&amp;quot;Shadow Color&amp;quot;
            value=&amp;quot;#000000&amp;quot;
          &amp;gt;&amp;lt;/knob-color&amp;gt;
          &amp;lt;knob-number
            id=&amp;quot;text-font-size&amp;quot;
            name=&amp;quot;Font Size&amp;quot;
            value=&amp;quot;16&amp;quot;
            suffix=&amp;quot;px&amp;quot;
          &amp;gt;&amp;lt;/knob-number&amp;gt;
          &amp;lt;knob-number
            id=&amp;quot;shape&amp;quot;
            name=&amp;quot;Border Radius&amp;quot;
            value=&amp;quot;100&amp;quot;
            suffix=&amp;quot;px&amp;quot;
          &amp;gt;&amp;lt;/knob-number&amp;gt;
          &amp;lt;knob-number
            id=&amp;quot;padding&amp;quot;
            name=&amp;quot;Padding&amp;quot;
            value=&amp;quot;12&amp;quot;
            suffix=&amp;quot;px&amp;quot;
          &amp;gt;&amp;lt;/knob-number&amp;gt;
        &amp;lt;/knob-group&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/html-element-sandbox&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are defining the markup we want to use in our sandbox. We are using the &lt;code&gt;html-element-sandbox&lt;/code&gt; component to create a sandbox for our HTML Element.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;html-element-sandbox&amp;gt;&amp;lt;/html-element-sandbox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each knob is defined by an &lt;code&gt;id&lt;/code&gt; and a &lt;code&gt;name&lt;/code&gt;. The &lt;code&gt;id&lt;/code&gt; is used to identify the knob in the &lt;code&gt;template&lt;/code&gt; and the &lt;code&gt;name&lt;/code&gt; is used to display the knob in the UI.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;knob-number
  id=&amp;quot;shape&amp;quot;
  name=&amp;quot;Border Radius&amp;quot;
  value=&amp;quot;30&amp;quot;
  suffix=&amp;quot;px&amp;quot;
&amp;gt;&amp;lt;/knob-number&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the element inside the &lt;code&gt;template&lt;/code&gt; we use &lt;code&gt;knob-*&lt;/code&gt; attributes to get the values of the knobs and set the attributes, CSS style or text content.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;!-- Attributes --&amp;gt;
&amp;lt;div knob-attr-disabled=&amp;quot;disabled&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;knob-boolean id=&amp;quot;disabled&amp;quot; name=&amp;quot;Disable&amp;quot; value=&amp;quot;false&amp;quot;&amp;gt;&amp;lt;/knob-boolean&amp;gt;

&amp;lt;!-- CSS Properties --&amp;gt;
&amp;lt;div knob-css-color=&amp;quot;fg-color&amp;quot; knob-css-background-color=&amp;quot;bg-color&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;knob-color id=&amp;quot;bg-color&amp;quot; name=&amp;quot;Background Color&amp;quot; value=&amp;quot;#ff0000&amp;quot;&amp;gt;&amp;lt;/knob-color&amp;gt;
&amp;lt;knob-color id=&amp;quot;fg-color&amp;quot; name=&amp;quot;Foreground Color&amp;quot; value=&amp;quot;#ffffff&amp;quot;&amp;gt;&amp;lt;/knob-color&amp;gt;

&amp;lt;!-- Text Content --&amp;gt;
&amp;lt;div knob-text=&amp;quot;content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;knob-string id=&amp;quot;content&amp;quot; name=&amp;quot;Text Content&amp;quot; value=&amp;quot;Hello World&amp;quot;&amp;gt;&amp;lt;/knob-string&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A single knob can point to multiple elements:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markup&quot;&gt;&amp;lt;html-element-sandbox&amp;gt;
  &amp;lt;template&amp;gt;
    &amp;lt;div id=&amp;quot;buttons&amp;quot;&amp;gt;
      &amp;lt;button
        knob-text=&amp;quot;label&amp;quot;
        knob-css-color=&amp;quot;fg-color&amp;quot;
        knob-css-background-color=&amp;quot;bg-color&amp;quot;
        knob-css-border-radius=&amp;quot;shape&amp;quot;
        knob-css-font-size=&amp;quot;text-font-size&amp;quot;
        knob-css-padding=&amp;quot;padding&amp;quot;
        knob-css---shadow-color=&amp;quot;shadow&amp;quot;
        knob-attr-raised=&amp;quot;raised&amp;quot;
        knob-attr-contenteditable=&amp;quot;contenteditable&amp;quot;
      &amp;gt;&amp;lt;/button&amp;gt;
      &amp;lt;mwc-button
        knob-attr-label=&amp;quot;label&amp;quot;
        knob-css---mdc-theme-on-primary=&amp;quot;fg-color&amp;quot;
        knob-css---mdc-theme-primary=&amp;quot;bg-color&amp;quot;
        knob-css---mdc-shape-small=&amp;quot;shape&amp;quot;
        knob-attr-raised=&amp;quot;raised&amp;quot;
        label=&amp;quot;My Button&amp;quot;
      &amp;gt;&amp;lt;/mwc-button&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;
      import &amp;quot;https://www.unpkg.com/@material/mwc-button@0.26.1/mwc-button.js?module&amp;quot;;
    &amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      button {
        --shadow-color: #000;
        --elevation: 3px;
        display: block;
        border: none;
        background-color: transparent;
        cursor: pointer;
        box-shadow: 0 var(--elevation) calc(var(--elevation) * 2) 0 var(--shadow-color);
      }
      mwc-button {
        --mdc-theme-on-primary: #000;
        --mdc-theme-primary: #fff;
        --mdc-shape-small: none;
      }
      #buttons {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        gap: 1rem;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/template&amp;gt;
  &amp;lt;div slot=&amp;quot;knobs&amp;quot;&amp;gt;
    &amp;lt;knob-string id=&amp;quot;label&amp;quot; name=&amp;quot;Label&amp;quot; value=&amp;quot;BUTTON&amp;quot;&amp;gt;&amp;lt;/knob-string&amp;gt;
    &amp;lt;knob-group name=&amp;quot;Style&amp;quot; expanded&amp;gt;
      &amp;lt;knob-color
        id=&amp;quot;bg-color&amp;quot;
        name=&amp;quot;Background Color&amp;quot;
        value=&amp;quot;#ff0000&amp;quot;
      &amp;gt;&amp;lt;/knob-color&amp;gt;
      &amp;lt;knob-color
        id=&amp;quot;fg-color&amp;quot;
        name=&amp;quot;Foreground Color&amp;quot;
        value=&amp;quot;#ffffff&amp;quot;
      &amp;gt;&amp;lt;/knob-color&amp;gt;
      &amp;lt;knob-color id=&amp;quot;shadow&amp;quot; name=&amp;quot;Shadow Color&amp;quot; value=&amp;quot;#000000&amp;quot;&amp;gt;&amp;lt;/knob-color&amp;gt;
      &amp;lt;knob-number
        id=&amp;quot;text-font-size&amp;quot;
        name=&amp;quot;Font Size&amp;quot;
        value=&amp;quot;16&amp;quot;
        suffix=&amp;quot;px&amp;quot;
      &amp;gt;&amp;lt;/knob-number&amp;gt;
      &amp;lt;knob-number
        id=&amp;quot;shape&amp;quot;
        name=&amp;quot;Border Radius&amp;quot;
        value=&amp;quot;30&amp;quot;
        suffix=&amp;quot;px&amp;quot;
      &amp;gt;&amp;lt;/knob-number&amp;gt;
      &amp;lt;knob-number
        id=&amp;quot;padding&amp;quot;
        name=&amp;quot;Padding&amp;quot;
        value=&amp;quot;12&amp;quot;
        suffix=&amp;quot;px&amp;quot;
      &amp;gt;&amp;lt;/knob-number&amp;gt;
    &amp;lt;/knob-group&amp;gt;
    &amp;lt;knob-group name=&amp;quot;Attributes&amp;quot; expanded&amp;gt;
      &amp;lt;knob-boolean id=&amp;quot;raised&amp;quot; name=&amp;quot;Raised&amp;quot; value=&amp;quot;false&amp;quot;&amp;gt;&amp;lt;/knob-boolean&amp;gt;
      &amp;lt;knob-list id=&amp;quot;contenteditable&amp;quot; name=&amp;quot;Content Editable&amp;quot; value=&amp;quot;false&amp;quot;&amp;gt;
        &amp;lt;option value=&amp;quot;true&amp;quot;&amp;gt;true&amp;lt;/option&amp;gt;
        &amp;lt;option value=&amp;quot;false&amp;quot;&amp;gt;false&amp;lt;/option&amp;gt;
      &amp;lt;/knob-list&amp;gt;
    &amp;lt;/knob-group&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/html-element-sandbox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;style&lt;/code&gt; and &lt;code&gt;script&lt;/code&gt; can be added to load extra content into the sandbox (e.g. a &lt;code&gt;script&lt;/code&gt; to load a web component).&lt;/p&gt;
&lt;h2&gt;Web Component&lt;/h2&gt;
&lt;p&gt;Before we update our component we need to rename &lt;code&gt;my-element.ts&lt;/code&gt; to &lt;code&gt;html-element-sandbox.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Open up &lt;code&gt;html-element-sandbox.ts&lt;/code&gt; and update it with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { css, html, LitElement } from &amp;quot;lit&amp;quot;;
import { customElement, state } from &amp;quot;lit/decorators.js&amp;quot;;

import &amp;quot;./knobs/boolean&amp;quot;;
import &amp;quot;./knobs/string&amp;quot;;
import &amp;quot;./knobs/number&amp;quot;;
import &amp;quot;./knobs/color&amp;quot;;
import &amp;quot;./knobs/list&amp;quot;;
import &amp;quot;./knobs/group&amp;quot;;
import { KnobValue } from &amp;quot;./knobs/base&amp;quot;;
import { BooleanKnob } from &amp;quot;./knobs/boolean&amp;quot;;

export const tagName = &amp;quot;html-element-sandbox&amp;quot;;

@customElement(tagName)
export class HTMLElementSandbox extends LitElement {
  static styles = css`
    main {
      --knobs-width: 300px;
      --code-height: calc(100% * 0.4);
      --mobile-height: 350px;
      display: grid;
      grid-template-areas: &amp;quot;preview&amp;quot; &amp;quot;knobs&amp;quot; &amp;quot;code&amp;quot;;
      grid-template-columns: 100%;
      grid-template-rows: var(--mobile-height) auto auto;
      height: 100%;
      width: 100%;
    }
    #preview {
      grid-area: preview;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      border-bottom: 1px solid #272727;
      background-color: whitesmoke;
    }
    @media (min-width: 600px) {
      main {
        grid-template-areas:
          &amp;quot;preview knobs&amp;quot;
          &amp;quot;code knobs&amp;quot;;
        grid-template-columns: calc(100% - var(--knobs-width)) var(
            --knobs-width
          );
        grid-template-rows: calc(100% - var(--code-height)) var(--code-height);
      }
      #preview {
        border-bottom: none;
      }
      slot[name=&amp;quot;knobs&amp;quot;] {
        overflow-y: auto;
      }
      pre {
        overflow-y: scroll;
      }
    }
    section {
      flex: 1;
    }
    slot[name=&amp;quot;knobs&amp;quot;] {
      grid-area: knobs;
      display: flex;
      flex-direction: column;
      border-left: 1px solid #000;
    }
    slot[name=&amp;quot;code&amp;quot;] {
      grid-area: code;
    }
    pre {
      margin: 0;
      font-family: Monaco, Courier, monospace;
      padding: 16px;
      background-color: #272727;
      color: #c8c8c8;
    }
    code {
      font-size: 0.8rem;
      white-space: pre-wrap;
    }
  `;

  @state() code = &amp;quot;&amp;quot;;

  render() {
    return html`&amp;lt;main&amp;gt;
      &amp;lt;section id=&amp;quot;preview&amp;quot;&amp;gt;
        &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
      &amp;lt;/section&amp;gt;
      &amp;lt;slot name=&amp;quot;knobs&amp;quot;&amp;gt; &amp;lt;/slot&amp;gt;
      &amp;lt;slot name=&amp;quot;code&amp;quot;&amp;gt;
        &amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;${this.code}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
      &amp;lt;/slot&amp;gt;
    &amp;lt;/main&amp;gt;`;
  }

  firstUpdated() {
    this.init();
  }

  init() {
    this.setUpKnobs();
    this.code = this.getCode();
    // Update the code every time a knob value changes
    this.addEventListener(&amp;quot;value&amp;quot;, () =&amp;gt; {
      this.code = this.getCode();
    });
  }

  setUpKnobs() {
    const root = this.shadowRoot!;
    const preview = root.getElementById(&amp;quot;preview&amp;quot;)!;
    const template = this.querySelector(&amp;quot;template&amp;quot;);
    if (template) {
      const div = document.createElement(&amp;quot;div&amp;quot;);
      div.appendChild(template.content.cloneNode(true));
      // Text Knobs (knob-text)
      div.querySelectorAll(&amp;quot;[knob-text]&amp;quot;).forEach((el) =&amp;gt; {
        const elemId = el.getAttribute(&amp;quot;knob-text&amp;quot;) || &amp;quot;&amp;quot;;
        const knob = this.querySelector(`#${elemId}`);
        if (knob &amp;amp;&amp;amp; knob instanceof KnobValue) {
          knob.addEventListener(&amp;quot;value&amp;quot;, () =&amp;gt; {
            const val = knob.value;
            el.textContent = val;
          });
          el.addEventListener(&amp;quot;input&amp;quot;, (e) =&amp;gt; {
            const target = e.target as HTMLElement;
            knob.value = target.textContent;
          });
          knob.init();
        }
      });
      div.querySelectorAll(&amp;quot;*&amp;quot;).forEach((el) =&amp;gt; {
        const attrs = el.attributes;
        for (let i = 0; i &amp;lt; attrs.length; i++) {
          const attr = attrs[i];
          const attrName = attr.name;
          // CSS Knobs (knob-css-*)
          if (attrName.startsWith(&amp;quot;knob-css-&amp;quot;)) {
            const cssKey = attrName.replace(&amp;quot;knob-css-&amp;quot;, &amp;quot;&amp;quot;);
            const knob = this.querySelector(`#${attr.value}`);
            if (
              knob &amp;amp;&amp;amp;
              knob instanceof KnobValue &amp;amp;&amp;amp;
              el instanceof HTMLElement
            ) {
              knob.addEventListener(&amp;quot;value&amp;quot;, () =&amp;gt; {
                const val = knob.value;
                if (knob.hasAttribute(&amp;quot;suffix&amp;quot;)) {
                  // Add suffix to the value (e.g. px)
                  el.style.setProperty(
                    cssKey,
                    val + knob.getAttribute(&amp;quot;suffix&amp;quot;)
                  );
                } else {
                  // No suffix, just set the value
                  el.style.setProperty(cssKey, val);
                }
              });
              knob.init();
            }
          }
          // Attribute Knobs (knob-attr-*)
          if (attrName.startsWith(&amp;quot;knob-attr-&amp;quot;)) {
            const attrKey = attrName.replace(&amp;quot;knob-attr-&amp;quot;, &amp;quot;&amp;quot;);
            const knob = this.querySelector(`#${attr.value}`);
            if (knob &amp;amp;&amp;amp; knob instanceof KnobValue) {
              knob.addEventListener(&amp;quot;value&amp;quot;, () =&amp;gt; {
                const val = knob.value;
                if (knob instanceof BooleanKnob) {
                  if (val) {
                    // &amp;lt;div hidden&amp;gt;
                    el.setAttribute(attrKey, &amp;quot;&amp;quot;);
                  } else {
                    // &amp;lt;div&amp;gt;
                    el.removeAttribute(attrKey);
                  }
                } else {
                  // &amp;lt;div value=&amp;quot;foo&amp;quot;&amp;gt;
                  el.setAttribute(attrKey, val);
                }
              });
              knob.init();
            }
          }
        }
      });
      preview.appendChild(div);
    }
  }

  getCode() {
    const root = this.shadowRoot!;
    const preview = root.getElementById(&amp;quot;preview&amp;quot;)!;
    if (preview.children.length &amp;gt; 0) {
      const child = preview.children[1];
      if (child &amp;amp;&amp;amp; child.children.length &amp;gt; 0) {
        const lines = this.elementToString(child.children[0]);
        // Trim empty lines
        const linesArray = lines.split(&amp;quot;\n&amp;quot;);
        const filteredLines = linesArray.filter((line) =&amp;gt; line.trim() !== &amp;quot;&amp;quot;);
        return filteredLines.join(&amp;quot;\n&amp;quot;);
      }
    }
    return &amp;quot;&amp;quot;;
  }

  elementToString(node: Element) {
    const sb: string[] = [];
    const tag = node.tagName.toLowerCase();
    sb.push(`&amp;lt;${tag}`);
    const attrs = node.attributes;
    // Add attributes
    for (let i = 0; i &amp;lt; attrs.length; i++) {
      const attr = attrs[i];
      if (attr.name.startsWith(&amp;quot;knob-&amp;quot;)) continue;
      // If the attribute is a boolean attribute, add it only if it&apos;s true
      if (attr.value === &amp;quot;&amp;quot;) {
        sb.push(` ${attr.name}`);
      } else {
        sb.push(` ${attr.name}=&amp;quot;${attr.value}&amp;quot;`);
      }
    }
    sb.push(&amp;quot;&amp;gt;&amp;quot;);
    if (node.childNodes.length &amp;gt; 0) {
      for (let i = 0; i &amp;lt; node.childNodes.length; i++) {
        const child = node.childNodes[i];
        // If the child is a text node, add the content
        if (child instanceof Text) {
          sb.push(child.textContent || &amp;quot;&amp;quot;);
        } else if (child instanceof Element) {
          // If the child is an element, recurse
          sb.push(this.elementToString(child));
        }
      }
    }
    sb.push(`&amp;lt;/${tag}&amp;gt;`);
    return sb.join(&amp;quot;\n&amp;quot;);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [tagName]: HTMLElementSandbox;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Knobs&lt;/h2&gt;
&lt;p&gt;First let up create a base class that will be used to create all other knobs. Create &lt;code&gt;src/knobs/base.ts&lt;/code&gt; and update with with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { css, html, LitElement, TemplateResult } from &amp;quot;lit&amp;quot;;
import { property } from &amp;quot;lit/decorators.js&amp;quot;;

export class Knob extends LitElement {
  constructor(name: string) {
    super();
    this.name = name;
  }

  @property() name: string;
}

export abstract class KnobValue&amp;lt;T&amp;gt; extends Knob {
  constructor(name: string, public val: T) {
    super(name);
    this._value = val;
    this.notify();
  }

  static styles = css`
    .knob {
      display: flex;
      flex-direction: row;
      align-items: center;
      padding: 0.5rem;
    }
    .knob label {
      flex: 1;
    }
  `;

  _value: T;

  get value(): T {
    return this._value;
  }

  set value(value: T) {
    this._value = value;
    this.notify();
  }

  notify() {
    const value = this.value;
    this.onValue(value);
    this.dispatchEvent(
      new CustomEvent(&amp;quot;value&amp;quot;, {
        detail: value,
        bubbles: true,
        composed: true,
      })
    );
    this.requestUpdate();
  }

  render() {
    return html`
      &amp;lt;div class=&amp;quot;knob&amp;quot;&amp;gt;
        &amp;lt;label&amp;gt;${this.name}&amp;lt;/label&amp;gt;
        ${this.buildInput()}
      &amp;lt;/div&amp;gt;
    `;
  }

  onValue(_val: T) {}

  init() {
    this.notify();
  }

  resolveValue(val: T) {
    return val;
  }

  abstract buildInput(): TemplateResult;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Boolean knob&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;src/knobs/boolean.ts&lt;/code&gt; and update with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { KnobValue } from &amp;quot;./base&amp;quot;;

import { html } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;

export const tagName = &amp;quot;knob-boolean&amp;quot;;

@customElement(tagName)
export class BooleanKnob extends KnobValue&amp;lt;boolean&amp;gt; {
  constructor(name: string, val: boolean) {
    super(name, val);
  }

  static styles = KnobValue.styles;

  @property({
    type: Boolean,
    attribute: &amp;quot;value&amp;quot;,
  })
  _value = false;

  buildInput() {
    return html`&amp;lt;input
      type=&amp;quot;checkbox&amp;quot;
      .checked=${this.resolveValue(this.value)}
      @change=${this.onChange}
    /&amp;gt;`;
  }

  onChange(e: Event) {
    const target = e.target as HTMLInputElement;
    this.value = target.checked;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [tagName]: BooleanKnob;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Number Knob&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;src/knobs/number.ts&lt;/code&gt; and update with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { KnobValue } from &amp;quot;./base&amp;quot;;

import { html } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;

export const tagName = &amp;quot;knob-number&amp;quot;;

@customElement(tagName)
export class NumberKnob extends KnobValue&amp;lt;number&amp;gt; {
  constructor(name: string, val: number) {
    super(name, val);
  }

  static styles = KnobValue.styles;

  @property({
    type: Number,
    attribute: &amp;quot;value&amp;quot;,
    converter: {
      fromAttribute: (val: string) =&amp;gt; parseFloat(val),
      toAttribute: (val: boolean) =&amp;gt; val.toString(),
    },
  })
  _value = 0;

  buildInput() {
    return html`&amp;lt;input
      type=&amp;quot;number&amp;quot;
      .valueAsNumber=${this.resolveValue(this.value)}
      @change=${this.onChange}
    /&amp;gt;`;
  }

  onChange(e: Event) {
    const target = e.target as HTMLInputElement;
    this.value = target.valueAsNumber;
  }

  resolveValue(val: number): number {
    return val;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [tagName]: NumberKnob;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;String Knob&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;src/knobs/string.ts&lt;/code&gt; and update with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { KnobValue } from &amp;quot;./base&amp;quot;;

import { html } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;

export const tagName = &amp;quot;knob-string&amp;quot;;

@customElement(tagName)
export class StringKnob extends KnobValue&amp;lt;string&amp;gt; {
  constructor(name: string, val: string) {
    super(name, val);
  }

  static styles = KnobValue.styles;

  @property({ type: String, attribute: &amp;quot;value&amp;quot; })
  _value = &amp;quot;&amp;quot;;

  buildInput() {
    return html`&amp;lt;input
      type=&amp;quot;text&amp;quot;
      .value=${this.resolveValue(this.value)}
      @input=${this.onChange}
    /&amp;gt;`;
  }

  onChange(e: Event) {
    const target = e.target as HTMLInputElement;
    this.value = target.value;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [tagName]: StringKnob;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Color Knob&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;src/knobs/color.ts&lt;/code&gt; and update with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { html } from &amp;quot;lit&amp;quot;;
import { customElement } from &amp;quot;lit/decorators.js&amp;quot;;
import { StringKnob } from &amp;quot;./string&amp;quot;;

export const tagName = &amp;quot;knob-color&amp;quot;;

@customElement(tagName)
export class ColorKnob extends StringKnob {
  buildInput() {
    return html`&amp;lt;input
      type=&amp;quot;color&amp;quot;
      .value=${this.resolveValue(this.value)}
      @input=${this.onChange}
    /&amp;gt;`;
  }

  resolveValue(value: string) {
    if (value &amp;amp;&amp;amp; value.startsWith(&amp;quot;--&amp;quot;)) {
      const style = getComputedStyle(document.body);
      const resolved = style.getPropertyValue(value);
      return resolved;
    }
    return value;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [tagName]: ColorKnob;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;List Knob&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;src/knobs/list.ts&lt;/code&gt; and update with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { KnobValue } from &amp;quot;./base&amp;quot;;

import { html } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;

export const tagName = &amp;quot;knob-list&amp;quot;;

@customElement(tagName)
export class ListKnob extends KnobValue&amp;lt;string&amp;gt; {
  constructor(name: string, val: string) {
    super(name, val);
  }

  static styles = KnobValue.styles;

  @property({
    type: String,
    attribute: &amp;quot;value&amp;quot;,
  })
  _value = &amp;quot;&amp;quot;;

  buildInput() {
    const options = this.getOptions();
    return html`&amp;lt;select @change=${this.onChange}&amp;gt;
      ${Array.from(options).map(
        (option) =&amp;gt;
          html`&amp;lt;option
            value=${option.value}
            .selected=${this.value === option.value}
          &amp;gt;
            ${option.textContent}
          &amp;lt;/option&amp;gt;`
      )}
    &amp;lt;/select&amp;gt;`;
  }

  getOptions() {
    const options = this.querySelectorAll(
      &amp;quot;option&amp;quot;
    ) as NodeListOf&amp;lt;HTMLOptionElement&amp;gt;;
    return Array.from(options);
  }

  onChange(e: Event) {
    const target = e.target as HTMLSelectElement;
    this.value = target.value;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [tagName]: ListKnob;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Group Knob&lt;/h3&gt;
&lt;p&gt;Create &lt;code&gt;src/knobs/group.ts&lt;/code&gt; and update with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { css, html } from &amp;quot;lit&amp;quot;;
import { customElement, property } from &amp;quot;lit/decorators.js&amp;quot;;
import { Knob } from &amp;quot;./base&amp;quot;;

export const tagName = &amp;quot;knob-group&amp;quot;;

@customElement(tagName)
export class GroupKnob extends Knob {
  constructor(name: string, knobs: Knob[] = []) {
    super(name);
    this.knobs = knobs;
  }

  static styles = css`
    details {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
    }
    details summary {
      padding: 0.5rem;
    }
  `;

  knobs: Knob[];

  @property({ type: Boolean }) expanded = false;

  render() {
    return html`&amp;lt;details ?open=${this.expanded}&amp;gt;
      &amp;lt;summary&amp;gt;${this.name}&amp;lt;/summary&amp;gt;
      &amp;lt;div class=&amp;quot;collection&amp;quot;&amp;gt;
        &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
        ${this.knobs.map((knob) =&amp;gt; html`${knob}`)}
      &amp;lt;/div&amp;gt;
    &amp;lt;/details&amp;gt;`;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [tagName]: GroupKnob;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If everything worked as expected, you should see the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../../assets/knobs_1_dvkg6xds73.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you want to learn more about building with Lit you can read the docs &lt;a href=&quot;https://lit.dev/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source for this example can be found &lt;a href=&quot;https://github.com/rodydavis/html-element-sandbox&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Async Preact Signals</title><link>https://rodydavis.com/web/preact-signals/</link><guid isPermaLink="true">https://rodydavis.com/web/preact-signals/</guid><description>Learn how to manage asynchronous data fetching (Promises) with Preact signals by using side effects, ensuring state integrity, and handling dependency changes through proper request cancellation.</description><pubDate>Tue, 28 Jan 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Async Preact Signals&lt;/h1&gt;
&lt;p&gt;When working with &lt;a href=&quot;https://github.com/preactjs/signals&quot;&gt;signals&lt;/a&gt; in Javascript, it is very common to work with async data from &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;Promises&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Async vs Sync&lt;/h2&gt;
&lt;p&gt;But unlike other state management libraries, signals do not have an &lt;em&gt;asynchronous&lt;/em&gt; state graph and all values must be computed &lt;em&gt;synchronously&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;When people first start using signals they want to simply add &lt;strong&gt;async&lt;/strong&gt; to the function callback but this breaks how they work under the hood and leads to &lt;strong&gt;undefined&lt;/strong&gt; behavior. ☹️&lt;/p&gt;
&lt;p&gt;Async functions are a leaky abstraction and force you to handle them all the way up the graph. Async is also not always better and can have a &lt;a href=&quot;https://madelinemiller.dev/blog/javascript-promise-overhead/&quot;&gt;performance impact&lt;/a&gt;. 😬&lt;/p&gt;
&lt;h2&gt;Working with Promises&lt;/h2&gt;
&lt;p&gt;We can still do so much with sync operations, and make it eaiser to work with common async patterns.&lt;/p&gt;
&lt;p&gt;For example when you make a &lt;strong&gt;http&lt;/strong&gt; request using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch&quot;&gt;fetch&lt;/a&gt;, you want to return the data in the &lt;strong&gt;Promise&lt;/strong&gt; and update some UI.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const el = document.querySelector(&apos;#output&apos;);
let postId = &apos;123&apos;;
fetch(`/posts/${postId}`).then(res =&amp;gt; res.json()).then(post =&amp;gt; {
    el.innerText = post.title;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when we add signals we can rerun the fetch everytime the post id changes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { effect, signal } from &amp;quot;@preact/signals-core&amp;quot;;

const el = document.querySelector(&apos;#output&apos;);
const postId =  signal( &apos;123&apos;);

effect(() =&amp;gt; {
    fetch(`/posts/${postId.value}`).then(res =&amp;gt; res.json()).then(post =&amp;gt; {
        el.innerText = post.title;
    });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is better, but now we need to handle stopping the previous request if the post id changes before the previous fetch completes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { effect, signal } from &amp;quot;@preact/signals-core&amp;quot;;

const el = document.querySelector(&apos;#output&apos;);
const postId =  signal( &apos;123&apos;);
let controller;

effect(() =&amp;gt; {
    if (controller) {
         controller.abort();
    }
    controller = new AbortController();
    const signal = controller.signal;
    try {
       fetch(`/posts/${postId.value}`, { signal }).then(res =&amp;gt; res.json()).then(post =&amp;gt; {
            el.innerText = post.title;
        }); 
    } catch (err) {
       // todo: show error message
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this still skips a lot of things we normally want to show like loading states and error states.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { effect, signal, batch } from &amp;quot;@preact/signals-core&amp;quot;;

const el = document.querySelector(&apos;#output&apos;);
const postId =  signal( &apos;123&apos;);
const postData = signal({});
const errorMessage = signal(&apos;&apos;);
const loading = signal(false);
let controller;

effect(() =&amp;gt; {
    if (controller) {
         controller.abort();
    }
    controller = new AbortController();
    const signal = controller.signal;
    batch(() =&amp;gt; {
       loading.value = true;
       errorMessage.value = &apos;&apos;;
       postData.value = {};
    });
    try {
       fetch(`/posts/${postId.value}`, { signal }).then(res =&amp;gt; res.json()).then(post =&amp;gt; {
            batch(() =&amp;gt; {
                 postData.value = post;
                 loading.value = false;
             });
        }); 
    } catch (err) {
        errorMessage.value = err.message;
    }
});
effect(() =&amp;gt;  {
    if (loading.value) {
        el.innerText = &apos;Loading...&apos;;
    } else if (errorMessage.value) {
        el.innerText = `Error: ${errorMessage.value}`;
    } else {
        el.innerText = postData.value.title;
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can show the proper states, but this is only for one request...&lt;/p&gt;
&lt;p&gt;We could wrap this up in a class to reuse or create a new type of signal that can work with asynchronous data.&lt;/p&gt;
&lt;h2&gt;AsyncState&lt;/h2&gt;
&lt;p&gt;We want to have a base class that we can make our loading states easily extend from:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export class AsyncState&amp;lt;T&amp;gt; {
  constructor() {}

  get value(): T | null {
    return null;
  }

  get requireValue(): T {
    throw new Error(&amp;quot;Value not set&amp;quot;);
  }

  get error(): any {
    return null;
  }

  get isLoading(): boolean {
    return false;
  }

  get hasValue(): boolean {
    return false;
  }

  get hasError(): boolean {
    return false;
  }

  map&amp;lt;R&amp;gt;(builders: {
    onLoading: () =&amp;gt; R;
    onError: (error: any) =&amp;gt; R;
    onData: (data: T) =&amp;gt; R;
  }): R {
    if (this.hasError) {
      return builders.onError(this.error);
    }
    if (this.hasValue) {
      return builders.onData(this.requireValue);
    }
    return builders.onLoading();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://dartsignals.dev/async/state/&quot;&gt;This class&lt;/a&gt; actually comes from a &lt;a href=&quot;https://github.com/rodydavis/signals.dart&quot;&gt;Dart port of preact signals&lt;/a&gt; I created.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This allows us to easily check if there is an actual value, error or if it is loading. It also provides an easy builder method to map the state to another value. 🤩&lt;/p&gt;
&lt;h3&gt;AsyncData&lt;/h3&gt;
&lt;p&gt;The loading state extends &lt;strong&gt;AsyncState&lt;/strong&gt; and passes the value in the constructor to the overriden methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export class AsyncData&amp;lt;T&amp;gt; extends AsyncState&amp;lt;T&amp;gt; {
  private _value: T;

  constructor(value: T) {
    super();
    this._value = value;
  }

  get requireValue(): T {
    return this._value;
  }

  get hasValue(): boolean {
    return true;
  }

  toString() {
    return `AsyncData{${this._value}}`;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;AsyncLoading&lt;/h3&gt;
&lt;p&gt;For the loading state we override the methods like &lt;strong&gt;AsyncData&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export class AsyncLoading&amp;lt;T&amp;gt; extends AsyncState&amp;lt;T&amp;gt; {
  get value(): T | null {
    return null;
  }

  get isLoading(): boolean {
    return true;
  }

  toString() {
    return `AsyncLoading{}`;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;AsyncError&lt;/h3&gt;
&lt;p&gt;For the error state we can pass an object of any type to return the error as value instead of throwing an exception (like Go).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export class AsyncError&amp;lt;T&amp;gt; extends AsyncState&amp;lt;T&amp;gt; {
  private _error: any;

  constructor(error: any) {
    super();
    this._error = error;
  }

  get error(): any {
    return this._error;
  }

  get hasError(): boolean {
    return true;
  }

  toString() {
    return `AsyncError{${this._error}}`;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;asyncSignal&lt;/h2&gt;
&lt;p&gt;Now we the state classes created, we can create a function to create an asynchronous signal with all the logic we talked about earlier.&lt;/p&gt;
&lt;p&gt;We need to show the sync value at any time and have a way to abort previous requests.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export function asyncSignal&amp;lt;T&amp;gt;(
  cb: () =&amp;gt; Promise&amp;lt;T&amp;gt;
): ReadonlySignal&amp;lt;AsyncState&amp;lt;T&amp;gt;&amp;gt; {
  const loading = new AsyncLoading&amp;lt;T&amp;gt;();
  const reset = Symbol(&amp;quot;reset&amp;quot;);
  const s = signal&amp;lt;AsyncState&amp;lt;T&amp;gt;&amp;gt;(loading);
  const c = computed&amp;lt;Promise&amp;lt;T&amp;gt;&amp;gt;(cb);
  let controller: AbortController | null;
  let abortSignal: AbortSignal | null;

  function execute(cb: Promise&amp;lt;T&amp;gt;, cancel: AbortSignal) {
    (async () =&amp;gt; {
      s.value = loading;
      try {
        const result = await new Promise&amp;lt;T&amp;gt;(async (resolve, reject) =&amp;gt; {
          if (cancel.aborted) {
            reject(cancel.reason);
          }
          cancel.addEventListener(&amp;quot;abort&amp;quot;, () =&amp;gt; {
            reject(cancel.reason);
          });
          try {
            const result = await cb;
            if (cancel.aborted) {
              reject(cancel.reason);
              return;
            }
            resolve(result);
          } catch (error) {
            reject(error);
          }
        });
        s.value = new AsyncData&amp;lt;T&amp;gt;(result);
      } catch (error) {
        if (error === reset) {
          s.value = loading;
        } else {
          s.value = new AsyncError&amp;lt;T&amp;gt;(error);
        }
      }
    })();
  }

  effect(() =&amp;gt; {
    if (controller != null) {
      controller.abort(reset);
    }
    controller = new AbortController();
    abortSignal = controller.signal;
    execute(c.value, abortSignal);
  });

  return s;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes it very easy to create multiple asynchronous signals and also use it anywhere else you have signals in the application like effects and computeds.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const el = document.querySelector(&apos;#output&apos;);
const postId =  signal(&apos;123&apos;);

const result = asyncSignal(() =&amp;gt; fetch(`/posts/${postId.value}`).then(res =&amp;gt; res.json()));

effect(() =&amp;gt; {
   el.innerText = result.value.map({
      onLoading: () =&amp;gt; &apos;Loading...&apos;,
      onError: (err) =&amp;gt; `Error: ${err}`,
      onData: (post) =&amp;gt; post.title, 
   });
});

postId.value = &apos;456&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I have started a Preact Signals GitHub discussion &lt;a href=&quot;https://github.com/preactjs/signals/discussions/648&quot;&gt;here&lt;/a&gt; and you can find a gist with the &lt;a href=&quot;https://gist.github.com/rodydavis/3b5266da2cc07f6574d425f5ce6e1e31&quot;&gt;final source code here&lt;/a&gt;. 🎉&lt;/p&gt;
&lt;p&gt;This has made working with asynchronous data a lot eaiser to work with and would love to hear your thoughts about ways to improve it 👀&lt;/p&gt;
&lt;p&gt;Also if you are curious about how Angular does asynchronous signals you can check out the &lt;a href=&quot;https://angular.dev/guide/signals/resource&quot;&gt;resource signal&lt;/a&gt; and the &lt;a href=&quot;https://justangular.com/blog/building-computed-async-for-signals-in-angular&quot;&gt;computedFrom/Async signal&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Flutter Input Output Preview</title><link>https://rodydavis.com/flutter/snippets/input-output-preview/</link><guid isPermaLink="true">https://rodydavis.com/flutter/snippets/input-output-preview/</guid><description>This Dart Flutter widget, `TwoPane`, creates a versatile, theme-aware scaffold designed to display dual-pane content for both mobile and desktop views.</description><pubDate>Sun, 19 Jan 2025 05:35:39 GMT</pubDate><content:encoded>&lt;h1&gt;Flutter Input Output Preview&lt;/h1&gt;
&lt;p&gt;First we need a two pane widget to properly render on mobile and desktop:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

class TwoPane extends StatefulWidget {
  const TwoPane({
    super.key,
    required this.primary,
    required this.secondary,
    required this.title,
    this.actions = const [],
    this.loading = false,
  });

  final (String, WidgetBuilder) primary, secondary;
  final List&amp;lt;Widget&amp;gt; actions;
  final String title;
  final bool loading;

  @override
  State&amp;lt;TwoPane&amp;gt; createState() =&amp;gt; _TwoPaneState();
}

class _TwoPaneState extends State&amp;lt;TwoPane&amp;gt; {
  bool darkMode = false;

  void toggleDarkMode() {
    if (mounted) {
      setState(() {
        darkMode = !darkMode;
      });
    }
  }

  ThemeData theme(Color color, Brightness brightness) {
    return ThemeData(
      brightness: brightness,
      colorScheme: ColorScheme.fromSeed(
        seedColor: color,
        brightness: brightness,
      ),
      useMaterial3: true,
    );
  }

  @override
  void didUpdateWidget(covariant TwoPane oldWidget) {
    if (oldWidget.loading != widget.loading ||
        oldWidget.title != widget.title ||
        oldWidget.actions != widget.actions) {
      if (mounted) setState(() {});
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return Theme(
      data: theme(Colors.purple, darkMode ? Brightness.dark : Brightness.light),
      child: Builder(builder: (context) {
        final (primaryTitle, primaryBuilder) = widget.primary;
        final (secondaryTitle, secondaryBuilder) = widget.secondary;
        return Scaffold(
            appBar: AppBar(
              title: Text(widget.title),
              centerTitle: false,
              actions: [
                IconButton(
                  tooltip: &apos;Toggle dark mode&apos;,
                  onPressed: toggleDarkMode,
                  icon: Icon(darkMode ? Icons.light_mode : Icons.dark_mode),
                ),
                ...widget.actions,
              ],
            ),
            body: LayoutBuilder(
              builder: (context, dimens) {
                if (dimens.maxWidth &amp;gt; 800 &amp;amp;&amp;amp; dimens.maxHeight &amp;gt; 600) {
                  return Column(
                    children: [
                      if (widget.loading) const LinearProgressIndicator(),
                      Expanded(
                        child: Row(
                          children: [
                            Flexible(
                              flex: 1,
                              child: primaryBuilder(context),
                            ),
                            Flexible(
                              flex: 1,
                              child: secondaryBuilder(context),
                            ),
                          ],
                        ),
                      ),
                    ],
                  );
                }
                return DefaultTabController(
                  length: 2,
                  child: Column(
                    children: [
                      SizedBox(
                        height: kToolbarHeight,
                        width: double.infinity,
                        child: TabBar(
                          tabs: [
                            Tab(text: primaryTitle),
                            Tab(text: secondaryTitle),
                          ],
                        ),
                      ),
                      if (widget.loading) const LinearProgressIndicator(),
                      Expanded(
                        child: TabBarView(
                          children: [
                            primaryBuilder(context),
                            secondaryBuilder(context),
                          ],
                        ),
                      ),
                    ],
                  ),
                );
              },
            ));
      }),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can pass some text fields for one pane to render an output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

import &apos;two_pane.dart&apos;;

class InputOutputPreview extends StatefulWidget {
  const InputOutputPreview({
    super.key,
    required this.title,
    required this.input,
    required this.output,
    required this.preview,
    required this.placeholder,
    this.actions = const [],
    this.codeTitle = &apos;Code&apos;,
    this.previewTitle = &apos;Preview&apos;,
    this.loading = false,
    this.lazy = false,
    this.previewSize = const Size(300, 700),
  });

  final (
    String,
    ValueChanged&amp;lt;(TextEditingController, TextEditingController)&amp;gt;
  ) input, output;
  final Widget? preview;
  final Widget placeholder;
  final String title;
  final List&amp;lt;Widget&amp;gt; actions;
  final String codeTitle, previewTitle;
  final Size? previewSize;
  final bool loading;
  final bool lazy;

  @override
  State&amp;lt;InputOutputPreview&amp;gt; createState() =&amp;gt; _InputOutputPreviewState();
}

class _InputOutputPreviewState extends State&amp;lt;InputOutputPreview&amp;gt; {
  final input = TextEditingController();
  final output = TextEditingController();
  String? lastInput;
  String? lastOutput;

  @override
  void initState() {
    super.initState();
    if (!widget.lazy) input.addListener(onInput);
    output.addListener(onOutput);
  }

  @override
  void dispose() {
    super.dispose();
    if (!widget.lazy) input.removeListener(onInput);
    output.removeListener(onOutput);
    input.dispose();
    output.dispose();
  }

  void onInput() {
    final (_, update) = widget.input;
    final str = input.text;
    if (lastInput == str) return;
    update((input, output));
    lastInput = str;
  }

  void onOutput() {
    final (_, update) = widget.output;
    final str = output.text;
    if (lastOutput == str) return;
    update((output, input));
    lastOutput = str;
  }

  @override
  Widget build(BuildContext context) {
    final (inputTitle, _) = widget.input;
    final (outputTitle, _) = widget.output;
    return TwoPane(
      title: widget.title,
      actions: widget.actions,
      loading: widget.loading,
      primary: (
        widget.codeTitle,
        (context) =&amp;gt; SizedBox(
              height: double.infinity,
              child: Column(
                children: [
                  Flexible(
                    child: Padding(
                      padding: const EdgeInsets.all(8),
                      child: Card(
                        child: ListTile(
                          title: Text(inputTitle),
                          subtitle: TextField(
                            maxLines: null,
                            controller: input,
                            expands: true,
                            decoration: InputDecoration(
                              isCollapsed: true,
                              border: InputBorder.none,
                              suffix: widget.lazy
                                  ? IconButton(
                                      onPressed: onInput,
                                      icon: const Icon(Icons.save),
                                      tooltip: &apos;Submit&apos;,
                                    )
                                  : null,
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                  Flexible(
                    child: Padding(
                      padding: const EdgeInsets.all(8),
                      child: Card(
                        child: ListTile(
                          title: Text(outputTitle),
                          subtitle: TextField(
                            maxLines: null,
                            controller: output,
                            expands: true,
                            decoration: const InputDecoration(
                              isCollapsed: true,
                              border: InputBorder.none,
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
      ),
      secondary: (
        widget.previewTitle,
        (context) =&amp;gt; Container(
              color: Theme.of(context).colorScheme.surfaceVariant,
              child: Builder(builder: (context) {
                if (widget.previewSize == null) {
                  return widget.preview ?? widget.placeholder;
                }
                return Center(
                  child: Material(
                    elevation: 8,
                    child: SizedBox.fromSize(
                      size: widget.previewSize,
                      child: widget.preview ?? widget.placeholder,
                    ),
                  ),
                );
              }),
            )
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Flutter Infinite Canvas</title><link>https://rodydavis.com/flutter/snippets/infinite-canvas/</link><guid isPermaLink="true">https://rodydavis.com/flutter/snippets/infinite-canvas/</guid><description>Learn how to build a customizable, multi-touch infinite canvas widget in Flutter using combinations of `InteractiveViewer` and `CustomMultiChildLayout`.</description><pubDate>Sun, 19 Jan 2025 05:33:00 GMT</pubDate><content:encoded>&lt;h1&gt;Flutter Infinite Canvas&lt;/h1&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;The following is an example of how to build an infinite canvas with &lt;a href=&quot;https://api.flutter.dev/flutter/widgets/InteractiveViewer-class.html&quot;&gt;InteractiveViewer&lt;/a&gt; and &lt;a href=&quot;https://api.flutter.dev/flutter/widgets/CustomMultiChildLayout-class.html&quot;&gt;CustomMultiChildLayout&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Blog post: &lt;a href=&quot;https://rodydavis.com/posts/flutter-multi-touch-canvas&quot;&gt;Create a multi touch canvas in Flutter&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:vector_math/vector_math_64.dart&apos; hide Colors;

void main() {
  final controller = WidgetCanvasController([
    WidgetCanvasChild(
      key: UniqueKey(),
      offset: Offset.zero,
      size: const Size(400, 800),
      child: Scaffold(
        appBar: AppBar(
          title: const Text(&apos;Scaffold 1&apos;),
        ),
        body: const Placeholder(),
      ),
    ),
    WidgetCanvasChild(
      key: UniqueKey(),
      offset: const Offset(200, 200),
      size: const Size(400, 800),
      child: Scaffold(
        appBar: AppBar(
          title: const Text(&apos;Scaffold 2&apos;),
        ),
        body: const Placeholder(),
      ),
    ),
  ]);
  runApp(MaterialApp(home: WidgetCanvas(controller: controller)));
}

class WidgetCanvas extends StatefulWidget {
  const WidgetCanvas({super.key, required this.controller});

  final WidgetCanvasController controller;

  @override
  State&amp;lt;WidgetCanvas&amp;gt; createState() =&amp;gt; WidgetCanvasState();
}

class WidgetCanvasState extends State&amp;lt;WidgetCanvas&amp;gt; {
  @override
  void initState() {
    super.initState();
    controller.addListener(onUpdate);
  }

  @override
  void dispose() {
    controller.removeListener(onUpdate);
    super.dispose();
  }

  void onUpdate() {
    if (mounted) setState(() {});
  }

  static const Size _gridSize = Size.square(50);
  WidgetCanvasController get controller =&amp;gt; widget.controller;

  Rect axisAlignedBoundingBox(Quad quad) {
    double xMin = quad.point0.x;
    double xMax = quad.point0.x;
    double yMin = quad.point0.y;
    double yMax = quad.point0.y;
    for (final Vector3 point in &amp;lt;Vector3&amp;gt;[
      quad.point1,
      quad.point2,
      quad.point3,
    ]) {
      if (point.x &amp;lt; xMin) {
        xMin = point.x;
      } else if (point.x &amp;gt; xMax) {
        xMax = point.x;
      }

      if (point.y &amp;lt; yMin) {
        yMin = point.y;
      } else if (point.y &amp;gt; yMax) {
        yMax = point.y;
      }
    }

    return Rect.fromLTRB(xMin, yMin, xMax, yMax);
  }

  @override
  Widget build(BuildContext context) {
    const inset = 2.0;
    return Listener(
      onPointerDown: (details) {
        controller.mouseDown = true;
        controller.checkSelection(details.localPosition);
      },
      onPointerUp: (details) {
        controller.mouseDown = false;
      },
      onPointerCancel: (details) {
        controller.mouseDown = false;
      },
      onPointerMove: (details) {},
      child: LayoutBuilder(
        builder: (context, constraints) =&amp;gt; InteractiveViewer.builder(
          transformationController: controller.transform,
          panEnabled: controller.canvasMoveEnabled,
          scaleEnabled: controller.canvasMoveEnabled,
          onInteractionStart: (details) {
            controller.mousePosition = details.focalPoint;
          },
          onInteractionUpdate: (details) {
            if (!controller.mouseDown) {
              controller.scale = details.scale;
            } else {
              controller.moveSelection(details.focalPoint);
            }
            controller.mousePosition = details.focalPoint;
          },
          onInteractionEnd: (details) {},
          minScale: 0.4,
          maxScale: 4,
          boundaryMargin: const EdgeInsets.all(double.infinity),
          builder: (context, viewport) {
            return SizedBox(
              width: 1,
              height: 1,
              child: Stack(
                clipBehavior: Clip.none,
                children: [
                  Positioned.fill(
                    child: GridBackgroundBuilder(
                      cellWidth: _gridSize.width,
                      cellHeight: _gridSize.height,
                      viewport: axisAlignedBoundingBox(viewport),
                    ),
                  ),
                  Positioned.fill(
                    child: CustomMultiChildLayout(
                      delegate: WidgetCanvasDelegate(controller),
                      children: controller.children.map((e) {
                        return LayoutId(
                            id: e,
                            child: Stack(
                              clipBehavior: Clip.none,
                              children: [
                                Positioned.fill(
                                  child: Material(
                                    elevation: 4,
                                    child: SizedBox.fromSize(
                                      size: e.size,
                                      child: e.child,
                                    ),
                                  ),
                                ),
                                if (controller.isSelected(e.key!))
                                  Positioned.fill(
                                    top: -inset,
                                    left: -inset,
                                    right: -inset,
                                    bottom: -inset,
                                    child: Container(
                                      decoration: BoxDecoration(
                                        border: Border.all(
                                          color: Colors.blue,
                                          width: 1,
                                        ),
                                      ),
                                    ),
                                  ),
                              ],
                            ));
                      }).toList(),
                    ),
                  ),
                ],
              ),
            );
          },
        ),
      ),
    );
  }
}

class GridBackgroundBuilder extends StatelessWidget {
  const GridBackgroundBuilder({
    super.key,
    required this.cellWidth,
    required this.cellHeight,
    required this.viewport,
  });

  final double cellWidth;
  final double cellHeight;
  final Rect viewport;

  @override
  Widget build(BuildContext context) {
    final int firstRow = (viewport.top / cellHeight).floor();
    final int lastRow = (viewport.bottom / cellHeight).ceil();
    final int firstCol = (viewport.left / cellWidth).floor();
    final int lastCol = (viewport.right / cellWidth).ceil();

    return Stack(
      clipBehavior: Clip.none,
      children: &amp;lt;Widget&amp;gt;[
        for (int row = firstRow; row &amp;lt; lastRow; row++)
          for (int col = firstCol; col &amp;lt; lastCol; col++)
            Positioned(
              left: col * cellWidth,
              top: row * cellHeight,
              child: Container(
                height: cellHeight,
                width: cellWidth,
                decoration: BoxDecoration(
                  border: Border.all(
                    color: Colors.grey.withOpacity(0.1),
                    width: 1,
                  ),
                ),
              ),
            ),
      ],
    );
  }
}

class WidgetCanvasDelegate extends MultiChildLayoutDelegate {
  WidgetCanvasDelegate(this.controller);
  final WidgetCanvasController controller;
  List&amp;lt;WidgetCanvasChild&amp;gt; get children =&amp;gt; controller.children;

  Size backgroundSize = const Size(100000, 100000);
  late Offset backgroundOffset = Offset(
    -backgroundSize.width / 2,
    -backgroundSize.height / 2,
  );

  @override
  void performLayout(Size size) {
    // Then draw the screens.
    for (final widget in children) {
      layoutChild(widget, BoxConstraints.tight(widget.size));
      positionChild(widget, widget.offset);
    }
  }

  @override
  bool shouldRelayout(WidgetCanvasDelegate oldDelegate) =&amp;gt; true;
}

class WidgetCanvasChild extends StatelessWidget {
  const WidgetCanvasChild({
    required Key key,
    required this.size,
    required this.offset,
    required this.child,
  }) : super(key: key);

  final Size size;
  final Offset offset;
  final Widget child;

  Rect get rect =&amp;gt; offset &amp;amp; size;

  WidgetCanvasChild copyWith({
    Size? size,
    Offset? offset,
    Widget? child,
  }) {
    return WidgetCanvasChild(
      key: key!,
      size: size ?? this.size,
      offset: offset ?? this.offset,
      child: child ?? this.child,
    );
  }

  @override
  Widget build(BuildContext context) {
    return child;
  }
}

class WidgetCanvasController extends ChangeNotifier {
  WidgetCanvasController(this.children);

  final List&amp;lt;WidgetCanvasChild&amp;gt; children;
  final Set&amp;lt;Key&amp;gt; _selected = {};
  late final transform = TransformationController();
  Matrix4 get matrix =&amp;gt; transform.value;
  double scale = 1;
  Offset mousePosition = Offset.zero;

  bool _mouseDown = false;
  bool get mouseDown =&amp;gt; _mouseDown;
  set mouseDown(bool value) {
    _mouseDown = value;
    notifyListeners();
  }

  bool isSelected(Key key) =&amp;gt; _selected.contains(key);

  bool get hasSelection =&amp;gt; _selected.isNotEmpty;

  bool get canvasMoveEnabled =&amp;gt; !mouseDown;

  Offset toLocal(Offset global) {
    return transform.toScene(global);
  }

  void checkSelection(Offset localPosition) {
    final offset = toLocal(localPosition);
    final selection = &amp;lt;Key&amp;gt;[];
    for (final child in children) {
      final rect = child.rect;
      if (rect.contains(offset)) {
        selection.add(child.key!);
      }
    }
    if (selection.isNotEmpty) {
      setSelection({selection.last});
    } else {
      deselectAll();
    }
  }

  void moveSelection(Offset position) {
    final delta = toLocal(position) - toLocal(mousePosition);
    for (final key in _selected) {
      final index = children.indexWhere((e) =&amp;gt; e.key == key);
      if (index == -1) continue;
      final current = children[index];
      children[index] = current.copyWith(
        offset: current.offset + delta,
      );
    }
    mousePosition = position;
    notifyListeners();
  }

  void select(Key key) {
    _selected.add(key);
    notifyListeners();
  }

  void setSelection(Set&amp;lt;Key&amp;gt; keys) {
    _selected.clear();
    _selected.addAll(keys);
    notifyListeners();
  }

  void deselect(Key key) {
    _selected.remove(key);
    notifyListeners();
  }

  void deselectAll() {
    _selected.clear();
    notifyListeners();
  }

  void add(WidgetCanvasChild child) {
    children.add(child);
    notifyListeners();
  }

  void remove(Key key) {
    children.removeWhere((e) =&amp;gt; e.key == key);
    notifyListeners();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
</content:encoded></item><item><title>Lightweight Flutter Animations</title><link>https://rodydavis.com/flutter/snippets/lightweight-animations/</link><guid isPermaLink="true">https://rodydavis.com/flutter/snippets/lightweight-animations/</guid><description>Use the specialized `AnimationWidget` abstract class to efficiently manage Flutter animations and continuous UI updates without relying on standard `setState` calls.</description><pubDate>Sun, 19 Jan 2025 05:45:19 GMT</pubDate><content:encoded>&lt;h1&gt;Lightweight Flutter Animations&lt;/h1&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;First we need to create the abstract class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;abstract class AnimationWidget&amp;lt;T extends StatefulWidget&amp;gt; extends State&amp;lt;T&amp;gt;
    with SingleTickerProviderStateMixin {
  Duration elapsed = Duration.zero;
  Duration delta = Duration.zero;
  late final Ticker ticker;
  BoxConstraints constraints = const BoxConstraints.tightFor();

  @override
  void initState() {
    super.initState();
    ticker = createTicker((elapsed) {
      delta = elapsed - this.elapsed;
      this.elapsed = elapsed;
      update(elapsed);
      if (mounted) setState(() {});
    });
    ticker.start();
    WidgetsBinding.instance.addPostFrameCallback(start);
  }

  @override
  void dispose() {
    ticker.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, dimens) {
      constraints = dimens;
      return paint(context, dimens);
    });
  }

  void start(Duration time) {}

  void update(Duration time);

  Widget paint(BuildContext context, BoxConstraints constraints);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will let us replace &lt;code&gt;State&lt;/code&gt; with &lt;code&gt;AnimationWidget&lt;/code&gt; and not need to call &lt;code&gt;setState&lt;/code&gt; to rebuild the ui.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;For the example we need an inline canvas painter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;class InlinePainter extends CustomPainter {
  InlinePainter({
    required this.draw,
    super.repaint,
  });

  final void Function(Canvas canvas, Size size) draw;

  @override
  void paint(Canvas canvas, Size size) {
    draw(canvas, size);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) =&amp;gt; true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the example using the new widget class:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter/scheduler.dart&apos;;

class SimpleExample extends StatefulWidget {
  const SimpleExample({Key? key}) : super(key: key);

  @override
  State&amp;lt;SimpleExample&amp;gt; createState() =&amp;gt; _SimpleExampleState();
}

class _SimpleExampleState extends AnimationWidget&amp;lt;SimpleExample&amp;gt; {
  var x = 0.0;
  var y = 0.0;
  var z = 0.0;

  @override
  void update(Duration time) {
    final t = delta.inMilliseconds / 1000;
    x += t;
    y += t;
    z += t;
  }

  @override
  Widget paint(BuildContext context, BoxConstraints constraints) {
    return Material(
      child: Center(
        child: Container(
          width: 100,
          height: 100,
          transform: Matrix4.identity()
            ..rotateX(x)
            ..rotateY(y)
            ..rotateZ(z),
          child: const Text(
            &apos;Hello World&apos;,
            style: TextStyle(
              fontSize: 30,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
</content:encoded></item><item><title>Flutter Master-detail view</title><link>https://rodydavis.com/flutter/snippets/master-detail-view/</link><guid isPermaLink="true">https://rodydavis.com/flutter/snippets/master-detail-view/</guid><description>Implement adaptive Master-detail views in Flutter using a reusable widget that handles multi-column desktop layouts while providing necessary push navigation for constrained mobile screens.</description><pubDate>Sun, 19 Jan 2025 05:38:59 GMT</pubDate><content:encoded>&lt;h1&gt;Flutter Master-detail view&lt;/h1&gt;
&lt;p&gt;When building mobile, desktop and web applications with Flutter often times you are faced with what to do with lists and the content when selected. Depending on the data you may have a list that renders another list before resolving to a detail view. On tablet or desktop this can be achieved with multi-column layouts.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On mobile you will still need to push to the details screen since the space is constrained.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How to build a &lt;a href=&quot;https://en.wikipedia.org/wiki/Master%E2%80%93detail_interface&quot;&gt;Master-detail&lt;/a&gt; view with Flutter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;

class MasterDetail&amp;lt;T&amp;gt; extends StatefulWidget {
  const MasterDetail({
    Key? key,
    required this.listBuilder,
    required this.detailBuilder,
    required this.onPush,
    this.emptyBuilder,
  }) : super(key: key);

  final Widget Function(BuildContext, ValueChanged&amp;lt;T?&amp;gt;, T?) listBuilder;
  final Widget Function(BuildContext, T, bool) detailBuilder;
  final void Function(BuildContext, T) onPush;
  final WidgetBuilder? emptyBuilder;

  @override
  State&amp;lt;MasterDetail&amp;lt;T&amp;gt;&amp;gt; createState() =&amp;gt; _MasterDetailState&amp;lt;T&amp;gt;();
}

class _MasterDetailState&amp;lt;T&amp;gt; extends State&amp;lt;MasterDetail&amp;lt;T&amp;gt;&amp;gt; {
  final selected = ValueNotifier&amp;lt;T?&amp;gt;(null);
  double? detailsWidth;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      primary: false,
      body: LayoutBuilder(
        builder: (context, dimens) {
          const double minWidth = 350;
          final maxWidth = dimens.maxWidth - minWidth;
          if (detailsWidth != null) {
            if (detailsWidth! &amp;gt; maxWidth) {
              detailsWidth = maxWidth;
            }
            if (detailsWidth! &amp;lt; minWidth) {
              detailsWidth = minWidth;
            }
          }
          return ValueListenableBuilder&amp;lt;T?&amp;gt;(
            valueListenable: selected,
            builder: (context, item, child) {
              final canShowDetails = dimens.maxWidth &amp;gt; 800;
              final showDetails = item != null &amp;amp;&amp;amp; canShowDetails;
              return Row(
                children: [
                  Expanded(
                    child: widget.listBuilder(context, (item) {
                      if (canShowDetails) {
                        selected.value = item;
                      } else {
                        selected.value = null;
                        if (item != null) widget.onPush(context, item);
                      }
                    }, selected.value),
                  ),
                  if (canShowDetails)
                    MouseRegion(
                      cursor: SystemMouseCursors.resizeLeftRight,
                      child: GestureDetector(
                        behavior: HitTestBehavior.opaque,
                        onHorizontalDragUpdate: (details) {
                          if (mounted) {
                            setState(() {
                              double w = detailsWidth ?? maxWidth;
                              w -= details.delta.dx;
                              // Check for min width
                              if (w &amp;lt; minWidth) {
                                w = minWidth;
                              }
                              // Check for max width
                              if (w &amp;gt; maxWidth) {
                                w = maxWidth;
                              }
                              detailsWidth = w;
                            });
                          }
                        },
                        child: const SizedBox(
                          width: 5,
                          height: double.infinity,
                          child: VerticalDivider(),
                        ),
                      ),
                    ),
                  if (canShowDetails)
                    SizedBox(
                      width: detailsWidth ?? maxWidth,
                      height: double.infinity,
                      child: showDetails
                          ? widget.detailBuilder(context, item, false)
                          : widget.emptyBuilder?.call(context) ??
                              const Center(
                                child: Text(&apos;Select a item to view details&apos;),
                              ),
                    ),
                ],
              );
            },
          );
        },
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This widget will size itself after layout and try to size the list as small as possible with the details filling up the rest. This is important for later when we nest multiple of these to create progressively adapting layouts.&lt;/p&gt;
</content:encoded></item><item><title>Flutter Markdown View with Material 3</title><link>https://rodydavis.com/flutter/snippets/markdown-view-material-3/</link><guid isPermaLink="true">https://rodydavis.com/flutter/snippets/markdown-view-material-3/</guid><description>Style a Flutter `flutter_markdown` widget to align with Material 3 design principles using custom theme styling.</description><pubDate>Sun, 19 Jan 2025 05:37:02 GMT</pubDate><content:encoded>&lt;h1&gt;Flutter Markdown View with Material 3&lt;/h1&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;How to style the &lt;a href=&quot;https://pub.dev/packages/flutter_markdown&quot;&gt;Flutter markdown&lt;/a&gt; widget with &lt;a href=&quot;https://m3.material.io/&quot;&gt;Material 3&lt;/a&gt; text and color styles:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter_markdown/flutter_markdown.dart&apos;;
import &apos;package:go_router/go_router.dart&apos;;
import &apos;package:markdown/markdown.dart&apos; as md;
import &apos;package:url_launcher/url_launcher.dart&apos;;

class MarkdownView extends StatelessWidget {
  const MarkdownView({
    Key? key,
    required this.markdown,
    this.textScaleFactor = 1,
  }) : super(key: key);

  final String markdown;
  final double textScaleFactor;

  @override
  Widget build(BuildContext context) {
    final colors = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;
    return Markdown(
        data: markdown,
        selectable: true,
        softLineBreak: true,
        onTapLink: (text, link, _) {
          final url = link ?? &apos;/&apos;;
          if (url.startsWith(&apos;http&apos;)) {
            launchUrl(Uri.parse(url));
          } else {
            context.push(url);
          }
        },
        extensionSet: md.ExtensionSet(
          md.ExtensionSet.gitHubFlavored.blockSyntaxes,
          [md.EmojiSyntax(), ...md.ExtensionSet.gitHubFlavored.inlineSyntaxes],
        ),
        styleSheet: MarkdownStyleSheet(
            textScaleFactor: textScaleFactor,
            p: textTheme.bodyLarge!.copyWith(
              fontSize: 16,
              color: colors.onSurface.withOpacity(0.72),
            ),
            a: TextStyle(
              decoration: TextDecoration.underline,
              color: colors.onSurface.withOpacity(0.72),
            ),
            h1: textTheme.displaySmall!.copyWith(
              fontSize: 25,
              color: colors.onSurface,
            ),
            h2: textTheme.headlineLarge!.copyWith(
              fontSize: 20,
              color: colors.onSurface,
            ),
            h3: textTheme.headlineMedium!.copyWith(
              fontSize: 18,
              color: colors.onSurface,
            ),
            h4: textTheme.headlineSmall!.copyWith(
              fontSize: 16,
              color: colors.onSurface,
            ),
            h5: textTheme.titleLarge!.copyWith(
              fontSize: 16,
              color: colors.onSurface,
            ),
            h6: textTheme.titleMedium!.copyWith(
              fontSize: 16,
              color: colors.onSurface,
            ),
            listBullet: textTheme.bodyLarge!.copyWith(
              color: colors.onSurface,
            ),
            em: const TextStyle(fontStyle: FontStyle.italic),
            strong: const TextStyle(fontWeight: FontWeight.bold),
            blockquote: TextStyle(
              fontStyle: FontStyle.italic,
              fontWeight: FontWeight.w500,
              color: colors.onSurfaceVariant,
            ),
            blockquoteDecoration: BoxDecoration(
              color: colors.surfaceVariant,
              borderRadius: BorderRadius.circular(4),
            ),
            code: const TextStyle(fontFamily: &apos;monospace&apos;),
            tableHead:
                const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
            tableBody:
                const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
            blockSpacing: 8,
            listIndent: 32,
            blockquotePadding: const EdgeInsets.all(8),
            h1Padding: const EdgeInsets.symmetric(vertical: 8),
            h2Padding: const EdgeInsets.symmetric(vertical: 8),
            h3Padding: const EdgeInsets.symmetric(vertical: 8),
            h4Padding: const EdgeInsets.symmetric(vertical: 8),
            h5Padding: const EdgeInsets.symmetric(vertical: 8),
            h6Padding: const EdgeInsets.symmetric(vertical: 8),
            codeblockPadding: const EdgeInsets.all(8),
            codeblockDecoration: BoxDecoration(
              borderRadius: BorderRadius.circular(4),
              color: colors.surfaceVariant,
            ),
            horizontalRuleDecoration: BoxDecoration(
              border: Border(
                top: BorderSide(
                  color: colors.outline.withOpacity(0.4),
                  width: 1,
                ),
              ),
            )),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Flutter Native HTTP Client</title><link>https://rodydavis.com/flutter/snippets/native-http-client/</link><guid isPermaLink="true">https://rodydavis.com/flutter/snippets/native-http-client/</guid><description>The provided Dart code demonstrates how to initialize a cross-platform HTTP client in Flutter, automatically selecting between specialized networking libraries (like Cronet for Android and Cupertino for iOS) to ensure optimal performance.</description><pubDate>Sun, 19 Jan 2025 05:39:48 GMT</pubDate><content:encoded>&lt;h1&gt;Flutter Native HTTP Client&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;package:cronet_http/cronet_http.dart&apos;;
import &apos;package:cupertino_http/cupertino_http.dart&apos;;
import &apos;package:device_info_plus/device_info_plus.dart&apos;;
import &apos;package:flutter/material.dart&apos;;
import &apos;package:http/http.dart&apos;;
import &apos;package:platform_info/platform_info.dart&apos;;

void main() async {
  var clientFactory = Client.new;
  final device = DeviceInfoPlugin();
  if (platform.isAndroid) {
    final engine = CronetEngine.build(
      cacheMode: CacheMode.memory,
      userAgent: (await device.androidInfo).model,
    );
    clientFactory = () =&amp;gt; CronetClient.fromCronetEngine(engine);
  } else if (platform.isCupertino) {
    clientFactory = CupertinoClient.defaultSessionConfiguration.call;
  }
  runWithClient(() =&amp;gt; runApp(const MyApp()),clientFactory);
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Flutter Stream Widget</title><link>https://rodydavis.com/flutter/snippets/stream-widget/</link><guid isPermaLink="true">https://rodydavis.com/flutter/snippets/stream-widget/</guid><description>Implement the `StreamWidget` pattern to manage reactive UIs in Flutter by integrating asynchronous data streams directly into the widget&apos;s build method, handling data, error, and loading states automatically.</description><pubDate>Sun, 19 Jan 2025 05:41:20 GMT</pubDate><content:encoded>&lt;h1&gt;Flutter Stream Widget&lt;/h1&gt;
&lt;p&gt;Work with streams directly in the build method of a &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; widget:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;

import &apos;package:flutter/widgets.dart&apos;;

abstract class StreamWidget extends StatefulWidget {
  const StreamWidget({Key? key}) : super(key: key);

  Stream&amp;lt;Widget&amp;gt; build(BuildContext context);

  void initState() {}

  void dispose() {}

  void reassemble() {}

  Widget? buildEmpty(BuildContext context) =&amp;gt; null;

  Widget? buildError(BuildContext context, Object? error) =&amp;gt; null;

  @override
  State&amp;lt;StreamWidget&amp;gt; createState() =&amp;gt; _StreamWidgetState();
}

class _StreamWidgetState extends State&amp;lt;StreamWidget&amp;gt; {
  @override
  void initState() {
    widget.initState.call();
    super.initState();
  }

  @override
  void dispose() {
    widget.dispose.call();
    super.dispose();
  }

  @override
  void reassemble() {
    widget.reassemble.call();
    super.reassemble();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: widget.build(context),
      builder: (context, snapshot) {
        if (snapshot.hasError) {
          final result = widget.buildError(context, snapshot.error);
          if (result != null) return result;
        }
        if (snapshot.hasData) {
          return snapshot.data!;
        } else {
          final result = widget.buildEmpty(context);
          if (result != null) return result;
        }
        return const SizedBox.shrink();
      },
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This could also be applied to Future widgets, but for reactive screens, streams are closer to what is actually happening.&lt;/p&gt;
&lt;h2&gt;Riverpod Example&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;import &apos;dart:async&apos;;

import &apos;package:flutter/material.dart&apos;;
import &apos;package:flutter_riverpod/flutter_riverpod.dart&apos;;
import &apos;package:riverpod_annotation/riverpod_annotation.dart&apos;;

part &apos;generated.g.dart&apos;;

@riverpod
class GeneratedWidget extends _$GeneratedWidget {
  @override
  Widget build(BuildContext context) {
    return const Text(&apos;Generated widget!&apos;);
  }
}

@riverpod
class StreamWidget extends _$StreamWidget {
  @override
  Stream&amp;lt;Widget&amp;gt; build(BuildContext context) async* {
    final controller = StreamController&amp;lt;int&amp;gt;();
    final timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      controller.add(timer.tick);
    });
    yield* controller.stream.map((event) =&amp;gt; Text(&apos;Stream widget: $event&apos;));
    timer.cancel();
    await controller.close();
  }
}

@riverpod
class FutureWidget extends _$FutureWidget {
  @override
  Future&amp;lt;Widget&amp;gt; build(BuildContext context) async {
    await Future.delayed(const Duration(seconds: 3));
    return const Text(&apos;Future completed!&apos;);
  }
}

class Example extends StatelessWidget {
  const Example({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            Consumer(builder: (context, ref, child) {
              final generated = ref.watch(generatedWidgetProvider(context));
              return generated;
            }),
            Consumer(builder: (context, ref, child) {
              final stream = ref.watch(streamWidgetProvider(context));
              return stream.when(
                data: (data) =&amp;gt; data,
                error: (error, stack) =&amp;gt; Text(error.toString()),
                loading: () =&amp;gt; const CircularProgressIndicator(),
              );
            }),
            Consumer(builder: (context, ref, child) {
              final future = ref.watch(futureWidgetProvider(context));
              return future.when(
                data: (data) =&amp;gt; data,
                error: (error, stack) =&amp;gt; Text(error.toString()),
                loading: () =&amp;gt; const CircularProgressIndicator(),
              );
            }),
          ],
        ),
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Building a DND Agent with ADK, Antigravity, and Gemini 3</title><link>https://rodydavis.com/podcast/tech-walks/building-a-dnd-agent-with-adk-antigravity-and-gemini-3/</link><guid isPermaLink="true">https://rodydavis.com/podcast/tech-walks/building-a-dnd-agent-with-adk-antigravity-and-gemini-3/</guid><description>Demonstrates the creation of a complex, rule-grounded Dungeons &amp; Dragons campaign agent using the Agent Development Kit (ADK), specialized agent roles, and advanced grounding techniques with Gemini 3.</description><pubDate>Thu, 04 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Building a DND Agent with ADK, Antigravity, and Gemini 3&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;B6TcfdpkmxU&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Nohe breaks down how he built a &amp;quot;Campaign Agent&amp;quot; for Dungeons &amp;amp; Dragons/Pathfinder. This system uses a loop of specialized agents—including a receptionist, storyteller, rules lawyer, and critic—to generate playable adventures grounded in actual rulebooks using Google Search grounding.&lt;/p&gt;
&lt;p&gt;We also explore the Agent Development Kit (ADK), discussing why we prefer it for building sequential and parallel agent loops, how it handles tools (including GitHub MCP), and how to visualize your agent&apos;s logic as a graph.&lt;/p&gt;
&lt;p&gt;In this episode:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;00:00 - Intro: What we do at Google (Firebase, Genkit, GenUI)&lt;/li&gt;
&lt;li&gt;01:22 - The state of AI Agents&lt;/li&gt;
&lt;li&gt;02:34 - Building a D&amp;amp;D Campaign Agent&lt;/li&gt;
&lt;li&gt;07:45 - The Architecture: Storyteller, Rules Lawyer &amp;amp; The Critic&lt;/li&gt;
&lt;li&gt;09:58 - Why loops are better than single-shot prompts&lt;/li&gt;
&lt;li&gt;12:41 - What is the ADK (Agent Development Kit)?&lt;/li&gt;
&lt;li&gt;16:16 - Using GitHub MCP and Tools&lt;/li&gt;
&lt;li&gt;18:25 - &amp;quot;That sounds like a Cron Job&amp;quot;: When NOT to use AI&lt;/li&gt;
&lt;li&gt;20:30 - Testing Agents and Vertex AI optimization&lt;/li&gt;
&lt;li&gt;29:45 - Sneak Peek: Offline-first syncing with Better Auth &amp;amp; PowerSync&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources &amp;amp; Topics Mentioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ADK: https://google.github.io/adk-docs/&lt;/li&gt;
&lt;li&gt;Firebase Genkit: https://firebase.google.com/products/genkit&lt;/li&gt;
&lt;li&gt;Model Context Protocol (MCP): https://modelcontextprotocol.io/&lt;/li&gt;
&lt;li&gt;Google Vertex AI: https://cloud.google.com/vertex-ai&lt;/li&gt;
&lt;li&gt;PowerSync (Offline-First Sync): https://www.powersync.com/&lt;/li&gt;
&lt;li&gt;Better Auth: https://www.better-auth.com/&lt;/li&gt;
&lt;li&gt;Pathfinder 2e (Archives of Nethys): https://2e.aonprd.com/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hosts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nohe: Working on Firebase, Genkit, and ADK.&lt;/li&gt;
&lt;li&gt;Rody: Working on Cross-platform full-stack AI and Generative UI.#AI #GoogleCloud #Genkit #ADK #SoftwareEngineering #DND #Pathfinder&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro: What we do at Google (Firebase, Genkit, GenUI)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Hi, I&apos;m Noey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:01&lt;/strong&gt; · And I&apos;m Rodie. And today we&apos;re filming our first podcast here at the Google Plex. So, uh, this will be the ver worst version of our podcast we&apos;re ever going to record, but we&apos;re really excited to talk about AI today. Uh, before we get started, um, Noey, what kind of projects are you working on? What do you do at Google?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:17&lt;/strong&gt; · So, uh, I work on Firebase, which is the platform as a service, you know, uh, uh, team or back end as a service, I should say. And we&apos;ve been working on uh authentication traditionally, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:30&lt;/strong&gt; · Security rules, o uh app check, that type of thing. But lately, I&apos;ve been doing a lot with Genkit and the ADK and AI and agents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · That&apos;s awesome. What uh languages do you like to work in?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · English.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:43&lt;/strong&gt; · That&apos;s a good one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · Good for AI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · No. Uh generally it&apos;s like Go, Typescript. Um I don&apos;t like Python, but to like write good ADK code recently, it&apos;s been a lot of drinking the Python Kool-Aid, you know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · Yeah, that&apos;s interesting. And we&apos;ll uh be talking a little bit more about ADK later. Um, and for those that don&apos;t know, my name is Rodie. I&apos;m also here at Google working on a bunch of AI stuff. Uh, currently focused on crossplatform full stack AI, uh, including generative UI, which is a kind of a new way to send dynamic components back to the client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · But today, I really wanted to chat with you about like how we build agents. uh agents seem to be like this thing that everybody&apos;s talking about that they&apos;re uh involved in so many different parts of the application whether you&apos;re building a a client side agent to work with data or something that&apos;s hosted on a server like it just seems like a lot but I really want to find a way that we can practically build agents and actually break them down and how to build them. Do you have any like thoughts on that?&lt;/p&gt;
&lt;h3&gt;The state of AI Agents&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:50&lt;/strong&gt; · Like are you working with any type of agents?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53&lt;/strong&gt; · Download anti-gravity. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · And just just say building me an agent, it would go boom, done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57&lt;/strong&gt; · So that&apos;s actually my CLI too, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · Yeah, that&apos;s like a that&apos;s a good point because uh when you&apos;re working with an IDE, you have a coding agent and it has access to tools. It has access to context and they&apos;re probably one of the most uh contextheavy types of agents you possibly can build. But I feel like going from that to how do I build something for users that they can use is a big step. So like have you explored anything where you can start to break that process apart and be able to call it like an API?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:31&lt;/strong&gt; · So uh I I had just finished wrapping up something uh and it what what it is is we play uh we play Dungeons and Dragons.&lt;/p&gt;
&lt;h3&gt;Building a D&amp;amp;D Campaign Agent&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:40&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · I I play Roie shows up and he&apos;s like, &amp;quot;Oh god, what is this?&amp;quot; Uh, and in in the game, uh, I DM&apos;d one time, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:48&lt;/strong&gt; · And when you DM, you build out a story, a collaborative story with a bunch of different people, and you always want to try and like include everybody at the table, make them feel welcome, have some overarching goal that you&apos;re headed towards and things like that. And in my head, I was like, how can I make an agent that makes adventures that we would want to play, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:08&lt;/strong&gt; · Yeah, totally. Uh because I&apos;m sure you you probably know this like the first time you showed up we played a game you&apos;re like what in the world is going on?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:15&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:15&lt;/strong&gt; · Like who are these people? Why are they talking like this way and you know all these other things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:19&lt;/strong&gt; · Um so maybe for people like me I really didn&apos;t understand the concept of D&amp;amp;D coming into it for the first time. So could you maybe explain like what is like a basic flow of like why you&apos;d even want an agent in that process anyway?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:34&lt;/strong&gt; · like uh like for me I I kind of just thought people got together and and and dressed stuff up and just did a bunch of stuff. But when I got into it, it&apos;s like it&apos;s like a really cool like systematic uh way to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:47&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:47&lt;/strong&gt; · So like there there&apos;s a story and there&apos;s like these rule books, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:50&lt;/strong&gt; · And so you can buy these pre-generated adventures. You can play them uh without doing anything. And I was like, what if we created an agent that has like some lore behind it, right? M. So, you give it some like general guidelines and then you let it fill in the details, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:05&lt;/strong&gt; · Uh, and then there&apos;s a rule system as well. So, you know, if you show up the first night you&apos;re playing ever and you want to be the wizard and you want to cast fireball right away, you&apos;re going to be disappointed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:15&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:16&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:16&lt;/strong&gt; · Because you got to like level up to get to a certain spot where you can cast fireball or you might not be able to do anything else, you know?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:22&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:22&lt;/strong&gt; · I mean, wizard level one, you can&apos;t do anything. Fair. Yeah. So, uh, so you show up the first night, you&apos;re all excited, can&apos;t do it. That that&apos;s that&apos;s the thing is like with with D and D and Pathfinder and all these other tabletop role playing games, you have this thing where there&apos;s a system of rules and all these other uh boundaries that you have to abide by.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:43&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:43&lt;/strong&gt; · Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:43&lt;/strong&gt; · And then once you have that uh you you can kind of fill in the rest with your own story. And yeah, uh going back to what I said earlier, like every player is at the table, they they create a character that has their own motivations, right? So, you&apos;re playing like an alchemist and your goal is to find I forget what it is. It&apos;s okay if you don&apos;t remember either. Yeah. My my character is Oz the wizard. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:08&lt;/strong&gt; · Um no relation. Uh and his goal is to like search the world for knowledge, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:16&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:16&lt;/strong&gt; · But, you know, we&apos;re playing this campaign called Abomination Bolts, and there&apos;s not really a lot of like knowledge finding other than being like, what&apos;s underground, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:26&lt;/strong&gt; · Yeah. So, uh, just to give you a little backstory, we&apos;re going into this vault and it&apos;s underground.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:33&lt;/strong&gt; · Yeah. And I&apos;m in the pursuit of knowledge. So, of course, I&apos;m going to be like, &amp;quot;What&apos;s underground?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:37&lt;/strong&gt; · Uh, but like you might have something that like you might have a character that says like, &amp;quot;Oh, I want to find out like who murdered my father or something like that.&amp;quot; Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:45&lt;/strong&gt; · And it&apos;s really hard to include those like side stories.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:50&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:50&lt;/strong&gt; · In the main quest, you Well, I think even more than that, it&apos;s like uh at least for me, it&apos;s hard to be like, &amp;quot;Okay, this is where my current state is. This is the state I want to get to.&amp;quot; And like, how do I do this along the way without just going the the fastest possible path to optimize for speed? How do I optimize for adventure? And I think that&apos;s kind of something you&apos;re exploring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:10&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:10&lt;/strong&gt; · So, uh the you know, 3:00 a.m.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:13&lt;/strong&gt; · text messages, that&apos;s why I&apos;m a little tired right now. But 3 a.m. text messages to you are, &amp;quot;Hey, Rodie, check this thing out that I built.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:19&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:19&lt;/strong&gt; · Uh, and it&apos;s it&apos;s a campaign agent, right? So, yeah, the when you have a bunch of adventures grouped together, it&apos;s called a campaign. Uh, and each the campaign agent takes your idea for what you want to accomplish in that game and it builds out a story that you can play and it it provides loot, progression, story progression, all these types of things that I honestly am just bad at because I I don&apos;t think I&apos;m very creative.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:45&lt;/strong&gt; · Uh, and it it fills in the gaps to allow everybody at your story not everybody at your story, everybody at your table to get a story that they&apos;d want to play and and understand and work through, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:56&lt;/strong&gt; · So, uh, for instance, this campaign agent, what it does is it tries to like sus out some details from me, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:02&lt;/strong&gt; · It&apos;s like, hey, uh, you want to play a campaign like what kind of campaign are you trying to play? Is it like a western? Is it uh like high adventure, high fantasy? Is it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:13&lt;/strong&gt; · And for people that don&apos;t know, like a D and D campaign is built around a theme and ultimately set of characters that has like a very specific set of rules to follow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:21&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:21&lt;/strong&gt; · Exactly. So, uh when when when it comes down to like trying to sus out those details, it it kind of sets the tone for how that agent will interact with you after that point. Right. So, uh so the campaign agent has this like receptionist and the receptionist is like, &amp;quot;All right, let me get some details and I&apos;m gonna hand you off to like the writing room.&amp;quot; Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:43&lt;/strong&gt; · And uh what happens in the writing room?&lt;/p&gt;
&lt;h3&gt;The Architecture: Storyteller, Rules Lawyer &amp;amp; The Critic&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:45&lt;/strong&gt; · Well, the writing room has a handful of people in there. It has a story agent who fills in the story. So uh you might want to like fight a dragon this adventure or something, right? So you say, &amp;quot;Oh, I want to fight a dragon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:58&lt;/strong&gt; · I don&apos;t have to know anything about dragons. I don&apos;t even have to come up with a story about why you&apos;re running into a dragon in in in the basement, right? Uh, but somehow you&apos;re going to run into a dragon and it it&apos;ll go and and say, &amp;quot;All right, you you run into this and and it kind of adds in those details that you don&apos;t have.&amp;quot; Then you have something that says like the character advancement. So, uh, you know, you have a character, I have a character, we all have our own goals in the game to advance our character storyline. So, let&apos;s say, uh, my character is in the pursuit of knowledge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:29&lt;/strong&gt; · I might find some ruby or gem in the basement that leads me into another side quest or another story.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:38&lt;/strong&gt; · Uh, and it&apos;s it&apos;s one of those things to allow your characters to like build on Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:42&lt;/strong&gt; · to what they have, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:44&lt;/strong&gt; · Well, one of the interesting things, too, is like you&apos;re building not only your character development, but also team development cuz like those side quests like at first like seem like, well, if we&apos;re just this is our goal, like why are we not just going straight there? like we can do the minimal number of operations per level to level up and whatever. And it&apos;s like it&apos;s really not about that. It&apos;s like exploring the unexpected turns, being able to like find new ways to uh you know, basically combat and like apply different methods and I feel like it&apos;s uh yeah, it&apos;s super cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:16&lt;/strong&gt; · How did these agents connect together? You know, like let&apos;s take a step back like um you have this idea for like what you want to build. Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:24&lt;/strong&gt; · You have this basically PRD for what these agents descriptions are. Like how did you go from taking that to code?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:31&lt;/strong&gt; · Like what what did you look at? Was there like something prior works for like maybe even existing solutions that just weren&apos;t meeting it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:39&lt;/strong&gt; · Oh yeah. I mean so I what I used to do was just single shot everything, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:43&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:43&lt;/strong&gt; · So hop hop into the Gemini app and say like it context stuff it just let it go. Fill in as much as you can. I want this and this and this and this and this. And that did like okay like the first few times.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:55&lt;/strong&gt; · Uh but like what I really got into and you know this because I talked to you about it ungodly amounts of times is that uh you can actually run all of these agents in like a loop. Right? So and and the way that I like to think about it is like Uncle Bob, right? He wrote a book about function declarations. And I always like to cite this when I think about agents because it&apos;s like what?&lt;/p&gt;
&lt;h3&gt;Why loops are better than single-shot prompts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;10:17&lt;/strong&gt; · Have one function do one thing and very tested. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:21&lt;/strong&gt; · Yeah. Nothing else. Right. Every time you have one that does one thing really well, leave it alone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:26&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:26&lt;/strong&gt; · Don&apos;t do that. Right. So I have I I started about the storyteller, the player advancement. Then I have the rules lawyer that comes in and like he writes some rules that says like oh like he&apos;s the enforcer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:36&lt;/strong&gt; · He&apos;s the enforcer, right? He knows like what&apos;s challenging for like six level one characters, right? Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:42&lt;/strong&gt; · Uh and he knows like how to balance things out and which he would know because uh D&amp;amp;D and Pathfinder have like a very extensive rule set open source.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:51&lt;/strong&gt; · Yeah. And so you can actually like just crawl it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:53&lt;/strong&gt; · Yeah. Exactly. So uh we use like Google search grounding in the one that I have.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:57&lt;/strong&gt; · Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:57&lt;/strong&gt; · That&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:58&lt;/strong&gt; · And you can actually plug in like the Pathfinder rule book and all which is great. You can just upload the PDF which Gemini is great at. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:03&lt;/strong&gt; · Yeah. It can fit the whole PDF too, right? Yes. Which I do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:07&lt;/strong&gt; · And notebook LM which is also awesome for it. So, uh, once you have that done, the final step that I have is the critic, right? And this like I tell it, yeah, I I start by giving each LLM that I interact with like a persona, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:23&lt;/strong&gt; · So, the first agent, I give it a persona. I say, you&apos;re the receptionist. You&apos;re trying to plan this out, whatever. The next one, I say like, you are, you know, the world&apos;s best storyteller, right? And you want to describe this world in all these different ways, right? And you keep going through these things. And then the final one is you are the world&apos;s biggest critic of people&apos;s homemade D&amp;amp;D adventures. You hate everything about them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:48&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:48&lt;/strong&gt; · Unless it meets like a certain bar of like adventure and quality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:52&lt;/strong&gt; · So you&apos;re almost building like a pessimistic uh gate to try to pass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:55&lt;/strong&gt; · Right. Yeah. And and then I have it give feedback and you tell it give me very specific and actionable feedback and give it a grade. M.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:03&lt;/strong&gt; · And if the grade is A+, if it&apos;s given to you as an A+ grade, you keep it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:08&lt;/strong&gt; · Oh, nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:09&lt;/strong&gt; · And you write it to disk, right? And then you exit the loop, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:12&lt;/strong&gt; · So I I run all these four or five agents except for the receptionist in this loop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:18&lt;/strong&gt; · So how can you do that? Cuz like I feel like that&apos;s one of the things like when I was building with Genkit or a bunch of other just maybe the API directly, I kind of had this idea and I felt like I was just building a lot of the boiler plate uh by myself. Uh what I&apos;ve been using recently, I didn&apos;t think I would like it, right? And then it turns out I really like it because I&apos;ve been doing it for everything is I&apos;ve been using the ADK.&lt;/p&gt;
&lt;h3&gt;What is the ADK (Agent Development Kit)?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;12:41&lt;/strong&gt; · Cool. And so what is ADK?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:43&lt;/strong&gt; · It is the agent development kit and it&apos;s available in Python, Go, and Java.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:49&lt;/strong&gt; · Sweet. Yeah. And then so um for those that may not know, it&apos;s actually, you know, fully open sourced and you can go check it out on GitHub. But why why does something like ADK exist? Like I feel like there&apos;s a lot of you know why not just use lang chain or like all these different adapters like or the API directly like um what are the things that you found in it that you&apos;re like oh this is like solving that thing that I was trying to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:15&lt;/strong&gt; · So I can&apos;t speak to lang chain because I&apos;ve never used it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:17&lt;/strong&gt; · Yeah. I&apos;ve heard about it. I know people like it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:20&lt;/strong&gt; · Fair. Great. Uh, but for me, the thing that I really like about it is how well it gives me like the tools that I need to build these like looping, sequential agents, all these types of things, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:32&lt;/strong&gt; · Which they call, I believe, workflow agents, do they?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:35&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:36&lt;/strong&gt; · Okay. Yeah. Oh, yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:38&lt;/strong&gt; · Sequential looping.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:39&lt;/strong&gt; · You read the docs right before we met, didn&apos;t you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:41&lt;/strong&gt; · Yeah. Sequential looping and something else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:44&lt;/strong&gt; · Parallel. Parallel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:44&lt;/strong&gt; · Parallel. Oh, yeah. Yeah. The parallel ones are cool, too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:46&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:47&lt;/strong&gt; · Yeah. I I should have used the parallel one for like the Yes. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:51&lt;/strong&gt; · Which is cool because you can say like here are these things that are independent and can happen and then you can do these all at once and do like a race. Um I feel like writing that kind of code in ADK made it a whole lot easier to see oh my agent is a graph and here&apos;s how these nodes are connected together whereas I didn&apos;t really find that solution with other stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:10&lt;/strong&gt; · And I know there&apos;s things like um lane chain graph which some people uh will probably recommend but I feel like ADK is like taking a agnostic approach to it and be like you can write it in whatever language you want. You can deploy it really easy on cloud run, you know, well, any languages you want, but basically anything beyond just JavaScript, you know, like that&apos;s fair.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:32&lt;/strong&gt; · You can uh you can bring these cloud first languages and you can deploy it on your own stuff, deploy it to cloud run really easily with a Docker container.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:40&lt;/strong&gt; · And I feel like that&apos;s where I was like, oh that that&apos;s like a really cool. And then also like the telemetry, the logging, all of that just makes it really easy. The dashboards, the dev tools. Yeah. One thing I do like about the ADK is because as you said it&apos;s like graph based. You actually get to see the graph.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:55&lt;/strong&gt; · You do and like on the events right when you when you get to see that like pop up in the dev tools is so awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:02&lt;/strong&gt; · And they also just launched uh an ADK visual builder where you can actually start to compose these together as well to help prototype that just came out. So we can Is it locally or is it I believe it is local. Yeah. So I can uh we can link the blog. But um I just I really like the concept of starting to think about agents and okay I have this idea I can start to really quickly to put these prompts into these individual nodes. Every node can have its own tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:28&lt;/strong&gt; · I think that&apos;s something that&apos;s a big unlock when they come to something like this when you know when you&apos;re used to just making it work as really good in a oneshot you&apos;re like here&apos;s like a hundred tools and here&apos;s all the access to stuff. But with ADK, you can be like if you&apos;re building a customer support bot, for example, like you always have to query the orders. You always have to make sure they have a valid account, you always and you you can guarantee that all the nodes will work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:55&lt;/strong&gt; · You&apos;re right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:55&lt;/strong&gt; · And every time it&apos;s either going to fail or it&apos;s not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:57&lt;/strong&gt; · And you can have like each node do something specific, right? So like I think there&apos;s some portion of it where it&apos;s like, okay, I I really do want this one to have 15 tools available to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:06&lt;/strong&gt; · Oh, totally. And like a bigger model and some you can have a really tiny model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:10&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:10&lt;/strong&gt; · Yeah. You know, it&apos;s funny that you bring up the the tools because I actually ran into, you know, a fire with this recently. I know where I I was trying out the GitHub MCP server inside the ADK and I was like, you know, hacking away, getting it to work and I was like, &amp;quot;All right, like figure out like the code to fix this bug, right?&amp;quot; Yes. I was testing uh Gemini 3 Pro preview. When I got done testing it, uh I set it up to like, oh, solve this thing for me. I was running it in like a looping agent.&lt;/p&gt;
&lt;h3&gt;Using GitHub MCP and Tools&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;16:36&lt;/strong&gt; · I gave it like 50 max iterations, some high number, which by the way, for people wondering, is a way to actually limit how many times this workflow runs. So you don&apos;t have to write any of the code. You can have built-in rate limiting, built-in like control over this thing will never exceed a a certain type of thing, which is very important when you&apos;re deploying an agent uh where you are not really as human in the loop and can control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:02&lt;/strong&gt; · Yeah. So uh I got up and I went to go get something for myself. Uh, probably something to eat, probably take the dog out for a walk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:10&lt;/strong&gt; · And when I came back, it had actually submitted a PR because I didn&apos;t realize that it had that much like I didn&apos;t realize the the tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:18&lt;/strong&gt; · I was like, I I don&apos;t even know. Like luckily, it was Gemini Pro 3, right? So, it did compile and run, fix the thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:24&lt;/strong&gt; · Fortunately, it didn&apos;t like ask anybody to review it. I got a ping from somebody. I was like, did you mean this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:31&lt;/strong&gt; · Wow. Yeah. I mean, that&apos;s a that&apos;s a cool call out because I feel like tools I mean, there&apos;s some recent uh studies that even show that like you can spend up to like 50k on tokens just in tools themselves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:42&lt;/strong&gt; · Oh, in the definitions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:43&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:43&lt;/strong&gt; · And there&apos;s like a new spec that&apos;s even kind of aiming to like dynamically find them. But I think that&apos;s kind of um kind of a more extreme side of it, but I feel like the ability to choose that like, hey, I know this customer support node is only ever going to need this tool or these three tools, right? And you can say you can support everything that this node can do. You can have a very specific prompt and you know that every time it calls it&apos;s going to work as expected and when it doesn&apos;t you can see the evals on why it&apos;s not and so you can write tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:14&lt;/strong&gt; · Now here&apos;s the interesting thing. You said every time you call this node you know it&apos;s going to work. However, that seems like it&apos;s deterministic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:22&lt;/strong&gt; · That&apos;s fair. Well and I I&apos;ve run into this issue where you know Jeff Yeah, I love Jeff. Jeff is great, but he grounds me to earth so fast sometimes.&lt;/p&gt;
&lt;h3&gt;&amp;quot;That sounds like a Cron Job&amp;quot;: When NOT to use AI&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;18:32&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:33&lt;/strong&gt; · I like we ran into this thing where we were writing and or I say we Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:37&lt;/strong&gt; · I was like, Jeff, I&apos;m gonna build this agent that does this, this, this, and this, and this. I&apos;ve gotten to here. He&apos;s like, you know, that sounds like a crown job. And I was like, thanks, Jeff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:47&lt;/strong&gt; · Thanks, Jeff. You&apos;re right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:49&lt;/strong&gt; · And I I think that there&apos;s something about, you know, obviously we&apos;re both very excited about Yeah. everything that you can do with AI and and plugging in the different bits and all these things. Like I&apos;m starting to build agents for myself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:03&lt;/strong&gt; · Oh, all the time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:04&lt;/strong&gt; · Every little task. I&apos;m like, you think in agents and you see agents as the hammer. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:09&lt;/strong&gt; · Yeah. It&apos;s like when when I first got uh into Android development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:12&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:13&lt;/strong&gt; · God, 10 years ago now. And I was like, oh my gosh, I&apos;m moving to an app that does this. Y and then I would show it to people and they&apos;re like it pen and paper. That&apos;s that&apos;s how notes work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:26&lt;/strong&gt; · It exists. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:27&lt;/strong&gt; · Yeah. Yeah. Stick it on the fridge. That&apos;s your to-do list, you know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:31&lt;/strong&gt; · And I just I got so amped up on that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:34&lt;/strong&gt; · And and I think there&apos;s there&apos;s a part where, you know, I think I&apos;m at fault at this. I don&apos;t know if you are, but you got to like pull back and be like, is am I actually doing the right thing here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:46&lt;/strong&gt; · See, like, am I I&apos;m installing Spanner for this like very light thing. Maybe there&apos;s another solution or or maybe like I don&apos;t want to use AI to do this thing. I could do deterministic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:57&lt;/strong&gt; · Right. Exactly. Yeah. So, when you go back to that uh that customer service example, Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:03&lt;/strong&gt; · do you think in your head, were you thinking deterministically or were you thinking something like you could give it like some kind of like rough guidelines? So, I kind of actually think that um maybe the way I like to approach AI is if you have good enough tests and you have good enough filters, if even if you have a bunch of bad responses, but only the good ones make it through. So, like is that considered success?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:28&lt;/strong&gt; · When you say tests, yes, define tests.&lt;/p&gt;
&lt;h3&gt;Testing Agents and Vertex AI optimization&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;20:32&lt;/strong&gt; · So, you could do testing with fuzzy or uh um automated integration or simulation style testing. M but really you want to say like and I think simulation testing is probably the best like analogous way to test AI for this where you say I have an agent or like basically a customer in this use case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:52&lt;/strong&gt; · This is the type of state they&apos;re in and you can say here are the tools that they have available here they can they can call the thing like do they how many times do they resolve to like a correct state. Oh, okay. So, you run through these scenarios almost like user journeys. And do we have another word for this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:07&lt;/strong&gt; · CJS or what were you thinking?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:09&lt;/strong&gt; · I was thinking evals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:11&lt;/strong&gt; · That&apos;s fair. That&apos;s a good point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:12&lt;/strong&gt; · Are you referring to evals?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:13&lt;/strong&gt; · I mean, it is a form of evals for sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:15&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:15&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:15&lt;/strong&gt; · Yeah. Yeah. Yeah. But it&apos;s one of those things where uh most of like what I work on right now is trying to get things like to a good enough running state where I manually test it. I know it&apos;s like the wrong way to do it though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:25&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:26&lt;/strong&gt; · And I really do want to get back into eval. And there&apos;s actually like a really cool vertex. Totally uh AI that remember we were trying to move to Dan and he said like you just plug in your prompt in the model and it&apos;ll like optimize your prompts for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:38&lt;/strong&gt; · Exactly. Yeah. There&apos;s so many cool tools now that we can use to help take our agents to the next level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:43&lt;/strong&gt; · I&apos;m wondering if that works for if it&apos;s on a per a like per prompt basis or if it&apos;s like on a per giant. I would assume it&apos;s a bare minimum of per prompt basis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:55&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:56&lt;/strong&gt; · But I&apos;m I&apos;m sure you could do it other ways too. So, getting it back to the the agent builder, um, what language did you decide to write it in? Because I feel like you have three options and you know, I know you may prefer Go, but I prefer Go. Uh, but it feels like the Go docs aren&apos;t necessarily there yet, which is very new to be clear as they came out last week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:19&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:20&lt;/strong&gt; · So, I was like or two weeks ago or something, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:22&lt;/strong&gt; · There&apos;s there&apos;s things in it that I feel like are there&apos;s things that might not be there. So I think it&apos;s also mapping a paradigm that doesn&apos;t exist necessarily one to one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:31&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:32&lt;/strong&gt; · But still does an incredible job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:33&lt;/strong&gt; · Oh yeah, it does. Uh I think the Python one is a little bit further ahead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:37&lt;/strong&gt; · Yeah. Which I believe it was the oldest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:40&lt;/strong&gt; · It was the first one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:41&lt;/strong&gt; · Yeah, that&apos;s what Yeah. Okay. Yeah, that&apos;s the oldest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:43&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:43&lt;/strong&gt; · And then Java Java was next. Um I chose just to go so I when I struggled with Go, I jumped out to Python. I remember doing I was doing like a You remember those training courses? They&apos;re like quick labs or something, I think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:56&lt;/strong&gt; · Yeah. Uh I was doing the training courses and I didn&apos;t know this but the Python one actually can wrap lang chain Yep. tools and other things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:06&lt;/strong&gt; · So I feel like it&apos;s a good base to set one totally because you are able to actually start from hey let me just do this like really basic and some existing work. There&apos;s a big ecosystem. Yeah. I feel like I was shocked uh when you sent me the example looking at the Python ADK code. Like it&apos;s amazing how much of the file is for your agent. Like like almost all of it&apos;s just your prompt and like a minimal function for like a tool or something like it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:33&lt;/strong&gt; · It really felt like it was not that much decoration or boiler plate. three lines of like maybe imports then like three lines saying like this is the agent, the name, the description and then 40 lines of the prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:47&lt;/strong&gt; · Yeah. Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:48&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:48&lt;/strong&gt; · Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:49&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:49&lt;/strong&gt; · It was it it&apos;s and it&apos;s you don&apos;t have to do anything special to call it either, right? It&apos;s just like ADK web and you get like the web UI and it spins up the agent all that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:59&lt;/strong&gt; · Totally. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:59&lt;/strong&gt; · Um with Go I think you have to like set up the launcher, the runner and then you call ADK or you say go run main.go agents.go go web UI like and you pass in like a bunch of arguments to get it to actually like launch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:13&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:13&lt;/strong&gt; · You know, I&apos;m sure it&apos;ll get better over time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:14&lt;/strong&gt; · Totally. And so like what are your next steps for this agent? Like do you feel like you did everything you set out to?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:21&lt;/strong&gt; · You feel like there&apos;s like some edge cases or do you feel like maybe an agent wasn&apos;t the right way to build it at all?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:28&lt;/strong&gt; · Like cuz I feel like sometimes we we need to look at it from all angles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:31&lt;/strong&gt; · So uh what the output was like good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:35&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:35&lt;/strong&gt; · I feel like it wasn&apos;t detailed enough though. So I wrote between like 230 and three and then it&apos;s like it works. I I want to get more details out of it. I actually want to like ground it in actual JSON data about Pathfinder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:50&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:50&lt;/strong&gt; · So there&apos;s like even a database you can connect to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:52&lt;/strong&gt; · Yeah. So I want to ground it in real data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:54&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:55&lt;/strong&gt; · Like you said from SQLite or wherever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:57&lt;/strong&gt; · Yeah. So you can use like the MCP toolbox and have SQLite Postgress my SQL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:01&lt;/strong&gt; · Yeah. And then because right now like when I ask it for stats, it will do the Google search.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:08&lt;/strong&gt; · But if you ever Google search Pathfinders things like, oh, I want like the wizard to get like a really cool weapon. Sometimes it&apos;ll pull from Pathfinder 1 or pull from Pathfinder 2 before the remastered edition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:18&lt;/strong&gt; · Well, for people that want like I there&apos;s actually revisions that like add new stuff and that was a big thing for me. I&apos;m like there&apos;s a lot of times in our campaign of like oh blah blah blah and it&apos;s probably like outdated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:28&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:28&lt;/strong&gt; · So, uh I want to pull from that. I think the storyteller just needs like a little bit of work. The mon the monster manual is probably one that I want to load in there. Right now, what I&apos;ve noticed is it creates monsters. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:39&lt;/strong&gt; · Rather than references something to like pull existing monsters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:43&lt;/strong&gt; · So, okay, maybe this is a good refactoring point with ADK. How easy is it to be to change out an agent to add a new agent in the middle of some kind of a process or even just to like approach it differently?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:55&lt;/strong&gt; · That is a great question. I think like dropping in and out of agents, yeah, is pretty easy, right? You just swap them out. So like when you have the ADK definition, Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:05&lt;/strong&gt; · of like your loop agent or whatever, there&apos;s something that says like sub agents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:10&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:10&lt;/strong&gt; · And it&apos;s an array of other agents. You can just drop them in and out. Pop them in. It&apos;s a graph. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:15&lt;/strong&gt; · Yeah. It&apos;s a graph, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:16&lt;/strong&gt; · Uh the one thing that is probably going to be hard about that is uh testing the results. So right now I don&apos;t test any of my agents in a vacuum.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:27&lt;/strong&gt; · So this is come comes back to the Vert.Ex AI uh service that will actually optimize your prompts and stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:33&lt;/strong&gt; · Y um or basically rewriting your prompts, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:36&lt;/strong&gt; · Rewriting your prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:37&lt;/strong&gt; · Yeah. Yeah. So I would probably want to jump into that service to get it to rewrite my prompts for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:45&lt;/strong&gt; · So that way I&apos;m not spending a bunch of time running this giant agent or like running every agent in isolation. And if there&apos;s something off the shelf that already works, I mean, that&apos;s the whole thing is we want to try to find things that let us focus on other stuff, but let us build our agents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:58&lt;/strong&gt; · Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:58&lt;/strong&gt; · Yeah. Yeah. So, I think I think it should be pretty easy, but it&apos;s just one of those things where um I haven&apos;t tried it yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:06&lt;/strong&gt; · Yeah, totally. Well, uh I feel like it was kind of a cool way to dive into an intro into agents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:13&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:14&lt;/strong&gt; · Uh there&apos;s I mean obviously a lot more that we have to talk about, but uh yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:18&lt;/strong&gt; · Any any other last thoughts on the the dungeon agent?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:22&lt;/strong&gt; · No, not off the top of my head. Um it was like a lot of fun to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:27&lt;/strong&gt; · Yeah. And really fast, too. I mean, like it really did not take that long. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:30&lt;/strong&gt; · You know, the one thing that I would do like not not the one thing I would do different next time, but like the next thing I think I&apos;m going to have to like do is stop myself from building so many.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:41&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:42&lt;/strong&gt; · Because like every time I pull out my laptop now, I&apos;ll start working on like doing one thing or some other thing. I&apos;m like, you know what? I bet I could write an agent that would take like an arbitrary input and I could just like say like, &amp;quot;Oh, go solve this thing.&amp;quot; It&apos;ll go and solve it. Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:54&lt;/strong&gt; · So, like Ash and I have one that we want to build for like her job and like Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:58&lt;/strong&gt; · There&apos;s there&apos;s like small little things that, you know, I just have a sledgehammer. I just want to hit it on everything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:04&lt;/strong&gt; · Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:05&lt;/strong&gt; · Yeah. So, that&apos;s awesome. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:07&lt;/strong&gt; · I&apos;m gonna have to like restrain myself from doing this, you know?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:10&lt;/strong&gt; · That&apos;s fair. I mean, I feel like uh it&apos;s awesome that we can build them so quickly and be able to deploy them. I mean, sometimes it only needs to exist for a period of time. I think that&apos;s actually one of those things with agents is like, do you build it for scale or do you build it for the task? And sometimes I&apos;m like, this thing will still save me time even if I&apos;m only using it for a week. Like it&apos;s like it&apos;s going to do everything I need. I mean, my process might change everything else, but like in this moment, it has the stuff it needs in the context. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:38&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:38&lt;/strong&gt; · Well, it&apos;s it&apos;s crazy. Like the the other day, Arthur was asking me for some like stats to be pulled and I was like, I know I I know I can write this myself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:47&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:47&lt;/strong&gt; · Let me just hand this off to anti-gravity and anti-gravity just built like this oneoff tool for me. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:52&lt;/strong&gt; · Well, what&apos;s what&apos;s crazy though is it&apos;s not even so much like I can do that myself. Sometimes it&apos;s like there are too many like smaller tasks that all depend on other tasks that sometimes it&apos;s just amount about like finding that data, filtering it down, doing a bunch of this boiler plate to where then you can come in and focus on it. One of the things I really love about anti-gravity is the uh agent manager. So you can literally be just kicking off that work, go on and do something else. Oh, we got a notification. It&apos;s done. Let me go do that report. Oh no, I need to change it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:20&lt;/strong&gt; · man. Well, I uh it&apos;s been awesome to be able to talk about uh uh ADK and agents and like kind of like where this space is heading and um anything you&apos;re going to be maybe researching in the next couple weeks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:33&lt;/strong&gt; · God, I don&apos;t know. I just get hit with so many things. I you know, there&apos;s a lot of things I&apos;m researching that I&apos;m not ready to like pull back the covers on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:41&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:42&lt;/strong&gt; · Maybe I&apos;ll have to listen for a future episode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:44&lt;/strong&gt; · Yeah, that&apos;d be great.&lt;/p&gt;
&lt;h3&gt;Sneak Peek: Offline-first syncing with Better Auth &amp;amp; PowerSync&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;29:46&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:46&lt;/strong&gt; · I feel like I&apos;m uh I do a lot of stuff in Dart and Go and C and SQLite and stuff like that. And uh lately I&apos;ve been exploring how to uh build an offline first syncing solution that uses better off for authentication with an optional login for people that don&apos;t know is a self-hosted off that connects to your database. So you could use fire store, you could use SQLite, Postgress, doesn&apos;t really matter. And then uh it uses power sync.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:12&lt;/strong&gt; · It allows you to like sync some type of data like in a really true deterministic way that uh lets you have offline. And the whole thing including the optional login, the optional sync was all built with anti-gravity. I built it over the course of probably 10 hours with Gemini 3 and I used some Gemini Deep Researches to be able to ground the context and I built this whole thing and it was so amazing how I could write the test coverage, I could create the plans, I could be very targeted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:43&lt;/strong&gt; · So, uh we&apos;ll have to listen for a future episode on a deep dive, but I have been really having a fun time with that. Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:49&lt;/strong&gt; · This was fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:50&lt;/strong&gt; · Yeah, absolutely. Yeah. Um, you can uh find all the links in the show description and please let us know if you have any other feedback on things you want to hear about. Uh, you can find us want to hear about maybe I&apos;m talking too much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:01&lt;/strong&gt; · Never talk about that again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:03&lt;/strong&gt; · Yeah. Uh, get that weird guy off of there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:04&lt;/strong&gt; · Totally. You can find someone near you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:06&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:08&lt;/strong&gt; · You can uh find us both on socials and of course here at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:11&lt;/strong&gt; · Cool. All right. See you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:12&lt;/strong&gt; · See you. Bye.&lt;/p&gt;
</content:encoded></item><item><title>Building with Antigravity, Gemini CLI, and Stitch</title><link>https://rodydavis.com/podcast/tech-walks/building-with-antigravity-gemini-cli-and-stitch/</link><guid isPermaLink="true">https://rodydavis.com/podcast/tech-walks/building-with-antigravity-gemini-cli-and-stitch/</guid><description>This tech podcast compares advanced AI development tools like Gemini CLI and Anti-Gravity, discusses modernizing infrastructure with K3s and home labs, and showcases workflows using NotebookLM and Stitch for context engineering and generative UI.</description><pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Building with Antigravity, Gemini CLI, and Stitch&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;09MTW1o42UI&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;In this next episode of our &amp;quot;untitled&amp;quot; podcast, Nohe and Rody take a &amp;quot;tech walk&amp;quot; to discuss the evolving landscape of AI development tools. We dive deep into the differences between the linear workflows of Gemini CLI and the asynchronous, project-level capabilities of Anti-Gravity.&lt;/p&gt;
&lt;p&gt;We also geek out on home lab setups—discussing the shift from Docker Compose to Kubernetes (K3s) on Raspberry Pi clusters—and share a game-changing workflow using NotebookLM to generate context files for your AI agents. Finally, we explore Stitch for generative UI, including how to instantly create shaders and animations from simple screenshots.&lt;/p&gt;
&lt;p&gt;Key Topics Discussed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gemini CLI vs. Anti-Gravity: Comparing agentic loops and managing complex refactors.&lt;/li&gt;
&lt;li&gt;Dev Environments: Configuring Neovim, Tmux, and using Git Worktrees to manage multiple AI tasks simultaneously.&lt;/li&gt;
&lt;li&gt;Infrastructure &amp;amp; DevOps: Running local K3s clusters, SSH tunneling with Anti-Gravity, and using AI to bridge the gap between Docker Compose and Kubernetes.&lt;/li&gt;
&lt;li&gt;Context Engineering: How to scrape documentation and use NotebookLM to write robust agent.md.&lt;/li&gt;
&lt;li&gt;Generative UI: Using Stitch to redesign legacy apps and generate complex Flutter shaders automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Chapters:
00:00 - Intro &amp;amp; Gemini CLI Configurations
01:05 - Gemini CLI vs. Anti-Gravity: Linear vs. Async Workflows
03:45 - Anti-Gravity’s Agent Manager &amp;amp; Project-Level Tasks
06:30 - Managing Multiple Contexts with Git Worktrees
08:30 - Neovim, Tmux, and AI Integration
12:30 - SSH Tunneling &amp;amp; Remote Development with Anti-Gravity
15:10 - Home Lab: Raspberry Pi Clusters &amp;amp; K3s vs. Docker Swarm
19:45 - Using AI to Convert Docker Compose to Kubernetes
22:55 - The NotebookLM Workflow: Generating Agent Rules from Docs
31:30 - Stitch: Generative UI, Shaders, and App Redesigns
35:20 - Outro &amp;amp; Teaser: Prompt Optimization&lt;/p&gt;
&lt;p&gt;Hosts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nohe: Working on Firebase, Genkit, and ADK.&lt;/li&gt;
&lt;li&gt;Rody: Working on Antigravity, Flutter, and Firebase Studio&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;#AI #GoogleCloud #GeminiCLI #Antigravity #SoftwareEngineering #NeoVim #Docker&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro &amp;amp; Gemini CLI Configurations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Hey, how&apos;s it going? I&apos;m Noey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:02&lt;/strong&gt; · And I&apos;m Rodie.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:03&lt;/strong&gt; · And we&apos;re here for our second Untitled podcast. We&apos;re thinking tech walks or something of that nature because we just happen to be walking outside when we record these. Uh, and today we wanted to talk about the Gemini CLI and the anti-gravity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:17&lt;/strong&gt; · Yeah, we are very excited about this episode. I can&apos;t wait to dive into the different ways we&apos;re using AI in our workflows and also Gemini 3.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:26&lt;/strong&gt; · Yeah. So, actually, did you know in the Gemini CLI you can do like slash settings and you can go through and set a whole bunch of different configuration options?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:35&lt;/strong&gt; · Oh, yeah, totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · So, one of the configurations options is to actually use the newer models. I don&apos;t know if that&apos;s just pinned to Gemini 3. I guess we&apos;ll find out when [clears throat] Gemini 4 or Gemini 5 or whatever comes out, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · Uh, but that&apos;s one way that you can quickly upgrade to a newer model to try it out in your workflow uh when it&apos;s whenever they introduce it, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:55&lt;/strong&gt; · Absolutely. Yeah. And uh we can definitely leave links down in the comments on how to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:59&lt;/strong&gt; · Yeah. So Gemini CLI came out. I&apos;ve been using it for the last few months and then anti-gravity came out like last month.&lt;/p&gt;
&lt;h3&gt;Gemini CLI vs. Anti-Gravity: Linear vs. Async Workflows&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:07&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07&lt;/strong&gt; · And I have not fully migrated to it yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · Oh, interesting. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · You have?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11&lt;/strong&gt; · I feel like I&apos;ve I&apos;ve completely done the opposite. I started with Gemini CLI uh the second it launched was really starting to use it heavily even inside of Firebase Studio when we included it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24&lt;/strong&gt; · Um, I felt like Gemini CLI was way more of the agent dev loop that I was kind of used to where I could have access to tools. I was not afraid to write bash scripts or uh be able to read and write files. I felt like I had control over I want you to work in this directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:43&lt;/strong&gt; · You know, it&apos;s funny because Gemini CLI, it will do one of those things where I&apos;m [clears throat] looking for a very specific file and I know that there&apos;s a bash command like set or or grab or whatever, but I know it exists. I don&apos;t know the command. So, Gemini CLI is really good at like just being like, &amp;quot;Oh, I know what you want to do.&amp;quot; And then sometimes it&apos;ll do like there&apos;s a said command to find and replace as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:08&lt;/strong&gt; · Oh, yeah. Yeah. So I I used that for like some documentation pages where we change like I you know all of our docs had one name and then we had to change it to another name. Oh yes. Yeah. So that was a very cool way to use the Gemini CLI. And the thing I like about the Gemini CLI is it kind of works how my brain works where I just go and I work linearly through a task. Yeah. Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:30&lt;/strong&gt; · And even there&apos;s a task feature now too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · Is there?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:33&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:34&lt;/strong&gt; · Built into Gemini CLI where basically it&apos;ll create like a task file. It&apos;ll show the list of things that it&apos;s working on and take it off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · So, the the task mode I I I haven&apos;t used that yet, but the way that I used tasks was uh I would have it write a plan first to say, &amp;quot;Hey, replace all my dependencies or upgrade this project or do something like that and then go through and give me a plan for how you would do it.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:59&lt;/strong&gt; · Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:59&lt;/strong&gt; · And then I and then I would feed it back and be like, &amp;quot;That plan sounds good. I didn&apos;t read it, but it sounds good. Go ahead and do that.&amp;quot; Right. Y because it usually kind of like prompts the model on the the expected steps in the workflow because by default it would kind of just like do the task and exit, you know, and and I found that I could get like more complicated prompts that way, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:20&lt;/strong&gt; · Uh and then occasionally when I use the CLI, I&apos;ll clear and start over and do some other things just to get like the things I&apos;m looking for and I continuously get commit, you know, after every test run, right? Yeah. Which it I think it even has checkpoints now, too. Does it? Oh, okay. That&apos;s great. So, uh you are a big fan of anti-gravity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:40&lt;/strong&gt; · Yes. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:41&lt;/strong&gt; · And I know that it works differently, right, with like the async loop and like you just kind of be like go do this thing. I&apos;m going to go drink some coffee, right?&lt;/p&gt;
&lt;h3&gt;Anti-Gravity’s Agent Manager &amp;amp; Project-Level Tasks&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:51&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:51&lt;/strong&gt; · So, I feel like with anti-gravity, um one of the big differences was uh Gemini CLI was kind of focused on I&apos;m solving this like one-off workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:00&lt;/strong&gt; · Yeah, I&apos;m doing this thing to fix whatever. But with anti-gravity, I can think at a project level. I&apos;m like, I want to migrate the state management or I want to switch from this database to a completely different provider or my approach and like the the complex refactoring is super awesome because it comes up with an implementation plan. It comes up with verification steps. It comes up with automated testing, manual testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:29&lt;/strong&gt; · It literally at the end of the loop it&apos;ll be okay now human you know go check this thing that you need to run through to verify that it&apos;s working and it&apos;s that that I just found like to be really really awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:42&lt;/strong&gt; · Yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:42&lt;/strong&gt; · it sounds like it. So one other question I had for you is you say that you give it this task and then you&apos;re able to like walk away. Do you do that through the agent manager or do you do it through like another type of thing?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:52&lt;/strong&gt; · So one of the cool UX pieces of it is it actually doesn&apos;t matter. You can do it from either side. So if you are in the normal editor and you have a chat and you ask it to kick off a task, you can hit commande or on Windows there&apos;s an equivalent and it will be both running in the agent manager and also inside of the editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:15&lt;/strong&gt; · So I can close out the editor if I want to and come back to it. Yeah, you&apos;re killing me. Because what I&apos;ve been doing is sometimes I I&apos;ll kick off a task in the chat like in the editor [laughter] and I&apos;ll be like, I don&apos;t want this to finish and I&apos;ll close the editor, but then I&apos;ll still get a popup and I&apos;m like, what happened?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:31&lt;/strong&gt; · Yeah, because it&apos;s totally running in the background. [laughter] Oh man, I feel I feel like a fool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:37&lt;/strong&gt; · So, uh uh pro tip, make sure [laughter] you not do that. Hit stop first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:43&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:44&lt;/strong&gt; · Yeah. And then there was another interesting thing and I wanted to understand how you did this because I don&apos;t fully understand how this works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:52&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:52&lt;/strong&gt; · So how do you approach multiple tasks in the same project? Because what I like to do which I know is wrong 100% know it&apos;s wrong is I will start a task in the agent manager.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:03&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:04&lt;/strong&gt; · Then switch to a different branch in my terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:08&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:08&lt;/strong&gt; · I I know the commands to do that. I don&apos;t have to ask Gemini CLI. I know how to do that. Uh, and then I go back to the agent manager and ask it to do a new task in the same project and it seems to have a hard time. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:21&lt;/strong&gt; · Yeah. I I was just miserable with it. So, like what what do you do to get that working? Because you were telling me about this yesterday.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:28&lt;/strong&gt; · Okay. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:28&lt;/strong&gt; · Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:29&lt;/strong&gt; · So, one of the things that I like to do is use a feature that&apos;s called git work trees. Uh, and you can do this by opening up the folder that contains your project and having it create a new git work tree for every piece of work that you&apos;re wanting to do. And for people that don&apos;t know, git work trees allow you to create a branch, check it out locally. And when you&apos;re working on the files, it will be doing it in isolation.&lt;/p&gt;
&lt;h3&gt;Managing Multiple Contexts with Git Worktrees&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;6:56&lt;/strong&gt; · So once it&apos;s done with it, it can just create a a merge commit back into the main. But you can use agent manager to open up each of the subfolders as subtasks and they can all be working together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:08&lt;/strong&gt; · So when you open up the agent manager, do you open up each project as a sub project?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:13&lt;/strong&gt; · I&apos;ve done it both ways and both work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:15&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:16&lt;/strong&gt; · So you can say like here is my orchestrator agent essentially. you&apos;re in charge of creating the git work trees and managing them and you know this is like the the high level like um you&apos;re in charge of merging and getting done with tasks and checking and then you can have sub agents that basically are opening up the specific folders and doing the tasks there as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:39&lt;/strong&gt; · Okay, that&apos;s interesting because I I&apos;ve never I&apos;ve never used GitWord trees but totally. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:44&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:45&lt;/strong&gt; · Yeah, they&apos;re super awesome. I definitely suggest people give them a try.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:48&lt;/strong&gt; · Yeah. And then how do you think you could do something similar with Gemini CLI?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:53&lt;/strong&gt; · Yeah, totally. I don&apos;t think this is like a workflow that&apos;s specific to Gravity in any way. Um, in fact, I was starting to experiment with uh using this in Gemini CLI. I will say the one thing that&apos;s really nice about anti-gravity is the agent manager where you can just clearly map them one to one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:13&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:13&lt;/strong&gt; · Yeah. cuz I I feel like in with the Gemini CLI, it still runs on your in your terminal. So maybe you just got to be comfortable with like a bunch of terminals open.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:21&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:22&lt;/strong&gt; · Which you could totally do with like T-Ux or something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · Well, I So I use T-Max with Gemini CLI and I used uh I have one terminal that&apos;s Gemini CLI and then another that&apos;s uh Neoim. Yeah, I was about to ask so like um anti-gravity like how does it compare to something like Neoim which like um I feel like Gemini CLI was kind of striking a nerve with you because it was closer to the terminal and also you know you kind of preferred some of those a different type of editor.&lt;/p&gt;
&lt;h3&gt;Neovim, Tmux, and AI Integration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;8:47&lt;/strong&gt; · Yeah. So I I like Neoim a lot because I SSH to my workstation. Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:53&lt;/strong&gt; · Totally. So, it&apos;s I&apos;m usually traveling or out and about and uh I have like a very nice Linux workstation in my like home man cave office type of thing, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:03&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:04&lt;/strong&gt; · Uh you&apos;ve seen it on the live streams. And um one [clears throat] thing that I have down there Oh, so I use like uh tail scale, I think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:12&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:12&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:12&lt;/strong&gt; · Tail scale to connect to it. But it it&apos;s funny. I went to my mother-in-law&apos;s for Thanksgiving and I was like, I&apos;m going to save power and shut down this machine. And then I&apos;m like up at 3 a.m. Ak I wound up and then just registering my sec my uh my public key in cloud, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:35&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:36&lt;/strong&gt; · So I got that working. I was like programming away. But it&apos;s it&apos;s one of those things where I use the Gemini CLI in one tab in my terminal or T-Mox setup and then I use Neo of them in the other. And the thing I like about anti-gravity specifically is the sidebar for like agent interactions because it shows you the diffs, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:56&lt;/strong&gt; · Whereas a lot of the time what I do actually I have three tabs open but the the first tab is uh Gemini CLI, the second&apos;s neoim and then the third is lazy git.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:05&lt;/strong&gt; · Oh, nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:06&lt;/strong&gt; · So I use lazy git to see the diffs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:08&lt;/strong&gt; · Nice. Yeah, I didn&apos;t try that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:09&lt;/strong&gt; · [laughter] And and then I accept the chunks. Yeah. Right. Because if I use lazy git or or if I use anti-gravity, excuse me, what I notice is that I can&apos;t actually accept any uh or I you get the chunks in the screen, right? You get the disc, but then as as soon as you switch to like a neoim Gemini CLI setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:31&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:31&lt;/strong&gt; · Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:32&lt;/strong&gt; · It&apos;s you&apos;re just like, &amp;quot;Hey, do this thing.&amp;quot; And then Gemini CLI will be like, &amp;quot;Oh, these are all the changes I want to make.&amp;quot; And they&apos;re generally too big to fit. And I haven&apos;t figured out how to set up scrolling in T-Mox because I&apos;m not like a very good like environment setup. Like I have to use Astro Vim because I don&apos;t want to write my own config files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:49&lt;/strong&gt; · Well, I even got Gemini CLI to set up my Neovent config for me. Like that was like one of Yeah. And it worked really really well. I was like, what were the decisions that it made?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:59&lt;/strong&gt; · Like I was like I in natural language said I want a Neoim setup that has a file tree already configured. I want line numbers and like blah blah blah. And like it totally downloaded the plugins and installed everything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:11&lt;/strong&gt; · Did you set the leader? Cuz isn&apos;t the leader normally like control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:16&lt;/strong&gt; · Yeah. I&apos;m I&apos;m not sure. Yeah. But I got it working and I was able to make a bunch of edits and Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:22&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:22&lt;/strong&gt; · I So I like Astroin because it the leader is space. They have docs and then uh if I move to a machine and I can&apos;t access my git profile or whatever, I could just go download it from there. Right. uh having like set up all these dot files. I did set up an Anible thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:40&lt;/strong&gt; · Oh, I love Ansible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:41&lt;/strong&gt; · So, so I will I will actually uh gosh I think it&apos;s called SCP to the machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:47&lt;/strong&gt; · Yeah. transfer the files over, run the Ansible playbook, which I have like a SH file for, and then I always just grab like the most basic OS that everybody has, Auntu or DBN or whatever, where it&apos;s not like I don&apos;t have to configure like a million things and then it just runs and I considered Nyx, but the problem with Nyx that I have is a lot of the times they don&apos;t have the packages that I want and I have to start building the packages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:16&lt;/strong&gt; · But I don&apos;t want to spend a lot of time building packages that you&apos;re not going to use. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:22&lt;/strong&gt; · Or that I have to maintain or and it&apos;s one of those things where I don&apos;t spin up a machine like every day. It&apos;s like once a month or so. Okay. Two things. One, have you tried SSH and anti-gravity? because I feel like what you&apos;re you&apos;re doing right now is using Neovm inside of the installed machine, but I&apos;ve been starting to just like SSH into my home lab directly, run all the things, and I can have my agent with an async manager with all the different nodes doing things like running my Antible playbooks, running scripts on a K3S cluster, stuff like that.&lt;/p&gt;
&lt;h3&gt;SSH Tunneling &amp;amp; Remote Development with Anti-Gravity&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;12:55&lt;/strong&gt; · Uh, this is where you&apos;re probably going to think I&apos;m like criminally insane, [laughter] but uh I will I I&apos;ll plug a keyboard into my phone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:04&lt;/strong&gt; · [laughter] Yes. And you did show me a demo with that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:07&lt;/strong&gt; · Yeah. Or like I&apos;ll plug it into the monitor and run in like desktop mode and I have like an app that I can use to SSH.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:12&lt;/strong&gt; · Oh yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:13&lt;/strong&gt; · It has like keys and all that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:15&lt;/strong&gt; · Totally. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:16&lt;/strong&gt; · And you can like Bluetooth your mouse into it. So like you still have a setup and then if I have a monitor, great. If I don&apos;t, I&apos;m just like Yeah. Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:24&lt;/strong&gt; · Granted, the screen&apos;s really tiny, so I don&apos;t do it all that often.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:27&lt;/strong&gt; · Sometimes I do it on like a like an iPad, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:30&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:30&lt;/strong&gt; · And then uh very rarely do I have like my personal laptop that I&apos;m comfortable sshing into okay my own my own machine. Right. So but so with anti-gravity you can SSH to another machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:46&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:46&lt;/strong&gt; · In fact you uh you know when you talked about setting up a temporary VM inside of Google Cloud you can just set that up and as long as you set up the SSH keys you can from anti-gravity connect to it. And in fact, anti-gravity was built for a lot of enterprise use cases in mine. So you can just totally have that workflow bin client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:06&lt;/strong&gt; · Not only that, it will tunnel the ports from the VM into your local host.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:12&lt;/strong&gt; · So I can be running a web server and using my local browser to to view it. And it&apos;s like the coolest thing. So you don&apos;t actually have to set up like so I believe the the SSH tunnel is usually like hyphen L and then you do like that port binding thing. You don&apos;t have to do any of that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:28&lt;/strong&gt; · No, you literally just give it the host. You log in as the SSH user or with your SSH keys and it it&apos;ll do a one-time setup on the uh server side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:36&lt;/strong&gt; · Oh, that is cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:37&lt;/strong&gt; · Yes, it is super awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:40&lt;/strong&gt; · I might have to go home and try it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:41&lt;/strong&gt; · Yeah, it is like right now. And it it changes how I approach like oh I want to do this thing and I know how to write this in docker compose maybe I can have anti-gravity write the kubernetes file and then deploy it to the cluster or I want to do this type of maintenance and so write the anible playbook go and apply it and it&apos;s like just approaching those kind of like devops types of things is so much more you have your own kubernetes cluster too don&apos;t you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:06&lt;/strong&gt; · Yes. And that is a Pi cluster if I&apos;m not mistaken.&lt;/p&gt;
&lt;h3&gt;Home Lab: Raspberry Pi Clusters &amp;amp; K3s vs. Docker Swarm&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;15:10&lt;/strong&gt; · It&apos;s a five node Raspberry Pi cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:12&lt;/strong&gt; · Almost all of them have the 16 gig RAM variant. So, uh I use it for my local hosted GitHub action runners. I host my parents websites, you know. It&apos;s it&apos;s really on that is it rough?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:25&lt;/strong&gt; · It&apos;s very solid. Like I I usually Are they armed?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:29&lt;/strong&gt; · Yeah, they&apos;re all armed. Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:30&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:30&lt;/strong&gt; · Yeah, that&apos;s interesting. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:32&lt;/strong&gt; · How long did it take you to set that up?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:33&lt;/strong&gt; · Um, honestly having the nodes in the rack did not take a long time. It was trying to land on what I wanted to do for deploying stuff on it. Um, originally I was using Docker with just like Docker Compose and then I switched to Docker Swarm. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:51&lt;/strong&gt; · But one of the things that&apos;s nice about K3S and the reason I migrated to it is um when you have things like a NAS or like uh a Synology or things like that, you can do persistent volume claims where it will manage the connecting to the storage and doing it for the cluster. Whereas in Docker Compose and Docker, it really is meant to be run on like a mini PC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:15&lt;/strong&gt; · They&apos;re not meant to be running clusters. like it can work but storage is kind of like an afterthought in networking like I had to like do this weird ingress with uh inject proxy manager and stuff like that. So see I I think I&apos;m the opposite of you where I just use Cloud Run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:34&lt;/strong&gt; · Cloud Run&apos;s amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:35&lt;/strong&gt; · It&apos;s cheap enough.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:37&lt;/strong&gt; · Yes. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:38&lt;/strong&gt; · Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:38&lt;/strong&gt; · And it like there&apos;s I very rarely do I ever get a bill from Cloud Run and I&apos;ve run some like Agreed. I&apos;ve run like big style transfer models and all this other stuff on there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:49&lt;/strong&gt; · A pie cluster. Maybe we should set one up one weekend. Just come down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:53&lt;/strong&gt; · Absolutely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:53&lt;/strong&gt; · We&apos;ll just buy some used Raspberry Pies cuz they&apos;re like what? $30 new. That&apos;s yours for my blood.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:59&lt;/strong&gt; · Uh except for the RAM prices now. They&apos;re all going up. But Oh, are they?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:02&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:03&lt;/strong&gt; · Oh, no.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:04&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:04&lt;/strong&gt; · I I think um being able to um approach a PI cluster has made me build with Cloud Run in a completely different way. Like I have many cloudr run instances, but sometimes it&apos;s hard to like prototype what you&apos;re wanting to build outside of running it. And so I can run something that I know is only going to last for a month a lot easier in the cluster. Whereas when I&apos;m going to do something for production, um like cloud run even supports like dock compose files now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:31&lt;/strong&gt; · So like I can have a multi-node thing all working together and I don&apos;t even have to change anything. I can just deploy and it&apos;s super awesome. The reason why I want a Kubernetes cluster is solely so that way I can run like Reddus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:45&lt;/strong&gt; · Oh yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:46&lt;/strong&gt; · Right. Like one of those things that don&apos;t work well in like the ephemeral cloud run like this is your machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:51&lt;/strong&gt; · Goodbye.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:52&lt;/strong&gt; · Yeah, that&apos;s fair.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:52&lt;/strong&gt; · Right. And uh I thought about like trying to hack together this weird like Reddis server setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:01&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:02&lt;/strong&gt; · But then it seems like what am I doing with my life if I&apos;m sitting there like okay I&apos;ll get like a mint micro instance and I&apos;ll do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:08&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:08&lt;/strong&gt; · and set all this up. But yeah, I also think if you can prove and validate the idea in a local environment, then it&apos;ll shine really well in a cloudr run environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:22&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:22&lt;/strong&gt; · So like uh you&apos;ve already like made a really good choice about what language you&apos;re using, the type of uh scaling around how you&apos;re compiling. Yeah. And I feel like when you&apos;re deploying a cloud run, you&apos;re you&apos;re committed to the idea, but it&apos;s like, you know, you want to have um some type of trajectory going into production where, you know, yes, there&apos;s many things that we deploy to Cloud Run that, you know, almost never get used, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:48&lt;/strong&gt; · But if you&apos;re like, how do I actually build an app that runs on Cloud Run and actually can handle traffic and do all these different things, you have to like really start to play with how am I what am I going to do about storage and caching like you said, Reddus and and all these other things all working together. Um, and so that&apos;s where like having a hybrid approach can be really good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:09&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:09&lt;/strong&gt; · plus just like you know Google does a lot with Kubernetes and so I feel like I just been wanting to learn that forever and so it&apos;s just been I&apos;ve been chipping away at the different layers of it and I feel like it&apos;s starting to click a little bit and uh hopefully future episodes we can know more too but uh like once you get into that world you&apos;re able to like start to think declaratively instead of imperatively so so does Kubernet do you use anti-gravity to set up your Kubernetes deployments and all Yes, because I I understand docker compose really well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:43&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:44&lt;/strong&gt; · And I can say this is how I would deploy this with docker compose.&lt;/p&gt;
&lt;h3&gt;Using AI to Convert Docker Compose to Kubernetes&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;19:48&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:49&lt;/strong&gt; · Write the kubernetes file for me from this docker compose file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:53&lt;/strong&gt; · And so from there it allows me to bridge what I am doing to where I want to be. And that&apos;s where I think AI is a great companion in that transformation of content and bridging that gap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:07&lt;/strong&gt; · Okay. So eventually I&apos;ll be able to write Kubernetes files directly. Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:12&lt;/strong&gt; · Yeah. So you&apos;re you&apos;re using it as I have this Docker Compose setup. I want to migrate it to Kubernetes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:19&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:20&lt;/strong&gt; · Here&apos;s like what my Do you tell it like your cluster information or anything or you just let Exactly. Or at least give it like a tool to find out the node status and other things like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:28&lt;/strong&gt; · Oh, okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:28&lt;/strong&gt; · So yeah. So, are you building your own tools that then you consume an anti-gravity through MCP or some other It&apos;s more like I give it a AI rules on like um here&apos;s my home lab setup with the things I installed and configured like you can run these commands on these servers to get health statuses and things like that and it can go and it it will totally SSH into the the the nodes run docker logs and it&apos;ll get out the status and it&apos;ll be uh giving you like a really cool way to work with the cluster. Like I can I can say things like um this service is down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:00&lt;/strong&gt; · Can you figure out why? And it&apos;ll be it&apos;ll go and say, &amp;quot;Oh, it&apos;s in an unhealthy state. Let me go try this thing. Oh, it&apos;s not connecting to this, you know, service and stuff like that.&amp;quot; Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:13&lt;/strong&gt; · Does anti-gravity automatically find your AI rules file or does it look for like a Gemini MD file?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:21&lt;/strong&gt; · Because Gemini CLI, I believe, yeah, has a preference towards Gemini.md files. Yes, I believe uh by default anti-gravity uses Gemini MD at the root of your project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:32&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:32&lt;/strong&gt; · Um but it&apos;s super easy to just tag it as context inside of um anti-gravity chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:39&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:39&lt;/strong&gt; · You can just like at whatever the name is and you can usually when I start a task I I will tag the readmes that I want it to do. Oh, if you have multiple rules files and then you just say like, hey, use AI rules for Kubernetes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:57&lt;/strong&gt; · Y okay, gotcha. Y and then how complicated are those rules files? Do you write them yourself?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:03&lt;/strong&gt; · So I usually what I like to do with AI too is I&apos;ll write the worst version and have AI write the better version. So, uh, I think I think we&apos;ve talked about this before outside of the podcast, but, um, I feel like now I will write an extremely ugly class. I&apos;ll get everything working. I&apos;ll write all the methods. I will write all of the the features. I know it technically works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:30&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:30&lt;/strong&gt; · But then what I do is I have it create the formatted class with the nicely documented. And uh by doing an approach like that, I can write my intent in this like short paragraph of like it needs to talk about this blah blah blah blah blah and uh it will use search grounding. It will use research to be able to expand that and then I save that as context in the file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:53&lt;/strong&gt; · So let me blow your mind.&lt;/p&gt;
&lt;h3&gt;The NotebookLM Workflow: Generating Agent Rules from Docs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;22:56&lt;/strong&gt; · Let&apos;s hear it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:57&lt;/strong&gt; · There&apos;s this tool that I use to write my gemini.md files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:00&lt;/strong&gt; · Let&apos;s hear it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:01&lt;/strong&gt; · It&apos;s called notebook ln.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:03&lt;/strong&gt; · Yeah, that&apos;s a good one. It&apos;s so good at writing the rules files cuz So tell me about that workflow. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:09&lt;/strong&gt; · Yeah. So what I do is I literally just import every piece of documentation I can find on the internet into a a a notebook, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:18&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:19&lt;/strong&gt; · Individual notebooks or do you like to like scope it like just this is my AR rules?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:25&lt;/strong&gt; · No, I I I have individual notebooks. So I had one for like firebased security rules. I had one for uh AI logic. I had one for like a bunch of different things that I was like, &amp;quot;Ah, let me just see like with no context, can it get me to the next level, right?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:40&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:40&lt;/strong&gt; · So, I imported like every piece of documentation I could find. I went to the reference docs. I imported that and then I went to notebook LM and you can get custom reports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:51&lt;/strong&gt; · Oh, I said write a agents.md file for me that goes indepth about how and when to implement this set of knowledge. Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:00&lt;/strong&gt; · Oh, cool. And when I tested out the one for Firebase security rules, but it gave me a very long report and I skimmed it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:08&lt;/strong&gt; · I was like, &amp;quot;This seems good enough, right?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:09&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:10&lt;/strong&gt; · And then I asked it to implement security rules in a project that didn&apos;t have them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:16&lt;/strong&gt; · It went through it and it was actually a sample project that I used, right? So the sample projects have like pretty good security rules. They say check for this, check for this, check for this. It wrote a set of security rules that had validation in it. Oh wow. Which is something I don&apos;t normally do when I write security rules. I&apos;m like, &amp;quot;Yeah, if like uh they can only insert like these fields and this, right?&amp;quot; But it was like it can only insert a integer in the star field between one and five.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:43&lt;/strong&gt; · Oh, yeah. Like literally almost like arc type or like um some type of structured validation. So, and I think that&apos;s like one of the things that is sometimes overlooked with security rules is that people will write a set of security rules, but if you know the schema and you know how to write to it, you could hypothetically inject any data you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:04&lt;/strong&gt; · As long as it fits the type. Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:06&lt;/strong&gt; · But as long as you have that that validation Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:09&lt;/strong&gt; · of like it has to be a string, it has to be a number, it can only be this long, blah blah blah blah blah blah blah, it will get to the point where it it really tightens it down. And then on top of that, you know what it did?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:20&lt;/strong&gt; · It added tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:22&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:22&lt;/strong&gt; · And then it rules to the rules. And then it loaded the emulator.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:26&lt;/strong&gt; · Wow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:26&lt;/strong&gt; · To then run the test for me, which probably you knew how to do in isolation, but never like an entire file on how to like describe this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:34&lt;/strong&gt; · When I was building the notebook LM Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:37&lt;/strong&gt; · bits, I went and I was just grabbing everything everything that I could find.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:42&lt;/strong&gt; · And one of the things I found was this like security rules testing framework, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:47&lt;/strong&gt; · Yeah. I was like, I&apos;ve never seen this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:49&lt;/strong&gt; · That&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:50&lt;/strong&gt; · And I it&apos;s like I feel like I should know about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:52&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:52&lt;/strong&gt; · So, when I discovered it, I was like, this is incredible. Why don&apos;t we talk about this more? And uh and then I just loaded it into my agent and it compiled and built and nearly worked first try.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:04&lt;/strong&gt; · So, when you&apos;re using Notebook LM to kind of find these resources, are you um trying to go out and paste a bunch of links in?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:13&lt;/strong&gt; · Are you uploading files that you have directly for the context? like what are you doing there?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:17&lt;/strong&gt; · I very rarely have a file on my laptop. It&apos;s mostly like, you know, photos I&apos;ve taken or things like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:23&lt;/strong&gt; · Uh I try and keep all my files online.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:26&lt;/strong&gt; · So I will go and I will grab links from different websites, okay, via Google search and insert them into the notebook or I&apos;ll crawl manually. There&apos;s probably an automated way to do this, fair, but I will crawl a page and grab the resource links that I want, which I have done that. Yeah, what I usually do is just write a script in Chrome DevTools and copy out all the A tags.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:50&lt;/strong&gt; · Rudy, what do [clears throat] you do?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:51&lt;/strong&gt; · [laughter] How do How do you just make your life so easy like that? [laughter] I&apos;m like, well, I&apos;m not going to manually copy all these, so I so I just Yeah. Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:03&lt;/strong&gt; · Well, the thing is I always like want to look at the page and be like, is this relevant? And then like pull it in. I see what you&apos;re saying. Like, oh, it&apos;s much easier to just write like a script tag, pull out the thing, drop the like do what is it? beautiful soup to like clean it out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:15&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:15&lt;/strong&gt; · Or like uh so there&apos;s two ways that I found that to be really useful. One is uh if I go to Firebase or Flutter or some type of doc site, they&apos;re going to have a sidenav with a bunch of links to every link on that site. I believe we support [snorts] um LM&apos;s text now. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:33&lt;/strong&gt; · And so we should have make that a lot easier. But before I could just write a simple script that went to the nav, grabbed all the address and then from there I could literally paste it as a block in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:43&lt;/strong&gt; · That sounds so much smarter than what I was doing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:46&lt;/strong&gt; · And like sometimes like I don&apos;t know what it is, but like the internet provider will just be like, &amp;quot;You seem to be navigating a lot. How about just slow down and read the content for once, bud?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:55&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:55&lt;/strong&gt; · You know, and then like maybe it&apos;s just my my internet service provider. Who knows? But I I Yeah, I I go to each page, look it over.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:05&lt;/strong&gt; · Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:06&lt;/strong&gt; · I do it the wrong way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:07&lt;/strong&gt; · Yeah, [laughter] you&apos;re the human in the loop on the finding the context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:11&lt;/strong&gt; · Oh, I&apos;m always a human in the loop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:12&lt;/strong&gt; · That&apos;s awesome. Well, that&apos;s that&apos;s a really cool use case with using it for writing the agent uh MD files. I have not been doing that and I might actually add that to my workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:22&lt;/strong&gt; · Yeah, it&apos;s well worth it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:24&lt;/strong&gt; · Yeah, totally. And plus it keeps the content up to date and a bunch of other reasons why that&apos;s important. Do you leave the like a bunch of different agent MD files in in your projects when you work with anti-gravity?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:36&lt;/strong&gt; · I do you selectively add and pull them in?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:39&lt;/strong&gt; · Yeah, I feel like I selectively bring them down. I try to think about context as a an artifact, but that it&apos;s not, you know, persistent, you know, like you can have this for the moment in time that you&apos;re needing it. So I I usually create like a research folder or a docs folder and I add in markdown files that are specific to you know deep researches that I&apos;ve run or um like like I could have one on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:03&lt;/strong&gt; · here&apos;s how to deploy with cloud run on all the configs on like a specific advanced kind of like storage and whatever and then I have a different one that&apos;s here&apos;s how to work with SQLite and set it up for the environment and that kind of allows me to tag the agent on those files when I&apos;m like okay we&apos;re working on the database base adapter like here go look at this thing and this is like the setup I want and then also here&apos;s a deep research on the schema that I&apos;m planning on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:27&lt;/strong&gt; · My my issue with like gemini.md files in the Google CLI Gemini CLI is that you you can nest them in different folders but then I feel like the context just gets clobbered over time. It just gets overwhelmed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:43&lt;/strong&gt; · Oh, 100%. That&apos;s why you have to be so specific on um either if you can try to paste it as content directly even if it&apos;s a subp part so I can like select it all and just paste it as markdown and the model will be able to read it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:57&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:57&lt;/strong&gt; · Um but otherwise yeah having multiple broken up files makes that a lot better cuz inside Gemini CLI for instance you can at other files with the at syntax and if you open up a file it&apos;ll basically just concatenate them together. So you can basically tag the lowest possible file that contains the context you need or you know contain you do an at file and then put in line numbers like does that work where it only extracts those line numbers or does it not that I found it might be supported but that would be cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:29&lt;/strong&gt; · I do know in anti-gravity you can select a range of part of a file and it will show as the context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:35&lt;/strong&gt; · I&apos;ve seen that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:36&lt;/strong&gt; · Yeah, which is super cool. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:37&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:37&lt;/strong&gt; · because then you&apos;re not like just sending over this huge file. And sometimes sometimes I like Gemini CLI will do smart things for me, right? So like one time I asked it like in this very large file like, &amp;quot;Hey, can you like do this thing?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:49&lt;/strong&gt; · And it found another method that already exists that did the thing I was looking for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:53&lt;/strong&gt; · Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:54&lt;/strong&gt; · It&apos;s what happens when you work in unfamiliar code bases, right? Or like you write code for like long enough and you&apos;re like, &amp;quot;Oh, I needed to like do this. Can you like help me just like stub out this method real quick?&amp;quot; And it&apos;s like, &amp;quot;Yeah, sure.&amp;quot; And then it&apos;ll like you write the stub and then it injects like the other method name like right in there and like I should take a nap, right? Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:13&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:14&lt;/strong&gt; · One thing that we talked about this morning in the live stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:18&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:18&lt;/strong&gt; · That I know that you&apos;ve been doing a lot of Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:20&lt;/strong&gt; · is you&apos;ve been pairing Stitch and anti-gravity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:23&lt;/strong&gt; · Oh, yeah. Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:24&lt;/strong&gt; · Yeah. Do you want to tell me how that&apos;s been working for you and like what you&apos;ve been doing with that?&lt;/p&gt;
&lt;h3&gt;Stitch: Generative UI, Shaders, and App Redesigns&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;31:30&lt;/strong&gt; · Totally. Um, one of the cool things about Stitch is it will generate both web apps and mobile apps and you know, you can make them responsive and get all the different um, HTML and images for it. But one of the nice things Stitch does is with Gemini 3, in addition to creating way better designs and I feel like uh, a lot closer to what I&apos;m wanting, it can also even redesign existing apps. So using Nano Banana Pro, you can say, &amp;quot;Here&apos;s my really terrible UI that I don&apos;t know how to write. Can you make it look better?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:03&lt;/strong&gt; · And uh one of the cool things about that flow is you take that, copy it into Gemini, and say, &amp;quot;Okay, for this image and possibly the HTML, generate me a Flutter app, generate me React or Angular, whatever.&amp;quot; It can like really effectively generate uh some really cool things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:21&lt;/strong&gt; · So, one of the apps that I&apos;ve been working on is a generative UI type of demo that includes some really cool features with ADK, which we talked about last week, and and some other exciting things with Gemini, but it&apos;s a very visually impressive demo, which requires a lot of gradients, a lot of design choices around uh animations and stuff like that. All of which usually take the majority of my time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:51&lt;/strong&gt; · Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:51&lt;/strong&gt; · And so what I&apos;ve been doing is having Gemini take those screenshots and it&apos;s been writing the gradients and the shaders in every part of the application including the animations like so effectively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:03&lt;/strong&gt; · Now when it writes the gradients does it write it as CSS or does it write it as here&apos;s an image that has a gradient set it as your background that I generated with Nano Banana.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:11&lt;/strong&gt; · What&apos;s even crazier is it&apos;s actually taking the screenshot that&apos;s inside the Gemini app and creating the gradients in Flutter. like it will write the flutter linear gradient from the colors in the image. Oh, and yeah, and it can use Nana Banana to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:28&lt;/strong&gt; · generate some uh image assets and I have done that, but I was like, wait, what if we just made this a shader like instead of a gradient, let&apos;s make this an animated mesh background and like anti-gravity is able to to take that context, write the shader code, set up the animation controllers, and you&apos;re able to all of a sudden unlock a workflow where whereas before, you know, I&apos;m not the strongest at writing shaders.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:54&lt;/strong&gt; · I I could get a basic version working, but now I could even just like prototype like, does it even work? Like, will this look good? And then, you know, it it was done in faster than it usually takes for me to even get started on getting the first version running. So, uh, I I feel like that workflow has just been so awesome to experiment with design choices that usually I&apos;m afraid of implementing because I know how much it&apos;s going to cost to keep those up to date or like reason about the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:22&lt;/strong&gt; · Usually have kind of geared towards simpler UIs or at least UIs that are more focused on UX. But now it&apos;s like, cool. Yeah, I&apos;ll do a really crazy design that involves multiple section headers and and different types of text elements and animations cuz I can just reiterate on it really quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:43&lt;/strong&gt; · Yeah. So, you&apos;re able to iterate over a bunch of different shaders.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:47&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:47&lt;/strong&gt; · Right. And and you you&apos;re saying you don&apos;t have to write a single line of code there? N it just does it, injects it, you&apos;re good to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:55&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:55&lt;/strong&gt; · and it will not only write and update the shader code, it will also write the logic that calls the shader, animates the uniforms, and connects it to the UI as well. So, it&apos;s just been super awesome. In this app that I&apos;m working on, I&apos;m up to three shaders in the app already. And I feel like that&apos;s already three more than I&apos;ve added in previous apps. So, uh I&apos;ve been super happy about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:17&lt;/strong&gt; · Yeah, that sounds incredible.&lt;/p&gt;
&lt;h3&gt;Outro &amp;amp; Teaser: Prompt Optimization&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;35:20&lt;/strong&gt; · All right, no. So, is there anything else that you want to kind of share that you&apos;ve been working on or excited about that you want to kind of close us out with?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:26&lt;/strong&gt; · Uh, there is something I&apos;ve been working on. It&apos;s uh surrounding prompting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:32&lt;/strong&gt; · Okay, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:32&lt;/strong&gt; · And prompt optimization, but I think I want to pull the covers back on that next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:37&lt;/strong&gt; · Sounds good. All right, cool. You have to stay tuned for that. Um, I feel like there has been so much to explore just in the last few weeks. Um, there anti-gravity and Gemini CLI and Stitch and so many other things we talked about are just like some amazing tools that I feel like just unlock these workflows that I I have been just waiting for. You know, I have been trying to to do a lot of these things and you get really far but then sometimes you just like hit the ceiling that you&apos;re just like I can&apos;t I can&apos;t break through like this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:07&lt;/strong&gt; · I&apos;m running into too much context or you know it&apos;s just a little bit too soon. But now I feel like a lot of those projects are I can pick up and like um I&apos;m able to get those done in like an afternoon whereas before it was like a week or so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:23&lt;/strong&gt; · I mean even just going back and reviving old projects, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:26&lt;/strong&gt; · Totally. Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:27&lt;/strong&gt; · Yeah. Things have been dead for months, you just hand it off to Jules and you&apos;re like, &amp;quot;Hey.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:30&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:30&lt;/strong&gt; · Make sure I can run this in today, you know?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:32&lt;/strong&gt; · Totally. And which I would suggest as a fun exercise for people, uh, go to your your old apps on GitHub, upload screenshots of them running into Stitch, have it generate a nice fresh new UI, open that up in anti-gravity, and just give it a nice redesign. Yeah. And you&apos;ll be surprised at how well that works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:52&lt;/strong&gt; · Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:53&lt;/strong&gt; · Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:54&lt;/strong&gt; · Well, thanks for uh listening to us yabber on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:57&lt;/strong&gt; · Sounds good. All right, see you next time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:59&lt;/strong&gt; · Ciao.&lt;/p&gt;
</content:encoded></item><item><title>Talking about Skills, Optimizing Prompts and building MCP servers in apps</title><link>https://rodydavis.com/podcast/tech-walks/talking-about-skills-optimizing-prompts-and-building-mcp-servers-in-apps/</link><guid isPermaLink="true">https://rodydavis.com/podcast/tech-walks/talking-about-skills-optimizing-prompts-and-building-mcp-servers-in-apps/</guid><description>Dive into advanced AI development, exploring new concepts like Agent Skills, optimizing prompts using Vertex AI, and implementing local application control via the Model Context Protocol (MCP) with Flutter.</description><pubDate>Wed, 04 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Talking about Skills, Optimizing Prompts and building MCP servers in apps&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;euyGjhHNP8w&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;We’re diving deep into the latest paradigms in AI development, starting with the difference between traditional context files (Gemini.md) and the new &amp;quot;Agent Skills&amp;quot; dynamic.&lt;/p&gt;
&lt;p&gt;We also share a story about using the Vertex AI Prompt Optimizer to automate our YouTube descriptions. It took 5 hours and nearly 100 million tokens, but the results were surprisingly consistent. Finally, we geek out on the Model Context Protocol (MCP), experimenting with exposing Flutter application state as local tools using Unix sockets.&lt;/p&gt;
&lt;p&gt;In this episode:&lt;/p&gt;
&lt;p&gt;0:00 - Intro: Why we are sitting today.
0:30 - What are Agent Skills? (Anthropic &amp;amp; Antigravity support) .
1:49 - Skills vs. Context: The difference between lazy loading and concatenating context files.
4:59 - How the API discovers and uses Skills.
7:25 - The open ecosystem for sharing and installing Skills.
10:30 - Deep Dive: Using Vertex AI Prompt Optimizer for YouTube descriptions.
13:28 - Converting CSVs to JSONL for data-driven optimization.
15:25 - The 100 Million Token experiment.
19:20 - Exploring MCP (Model Context Protocol) with Flutter.
22:03 - Using Unix Sockets vs. HTTP for local app control.
24:25 - Implementing the MCP spec in Antigravity.&lt;/p&gt;
&lt;p&gt;Resources mentioned:&lt;/p&gt;
&lt;p&gt;Open Skills Domain: https://agentskills.io/&lt;/p&gt;
&lt;p&gt;Vertex AI Prompt Optimizer (Colab Notebooks): https://cloud.google.com/blog/products/ai-machine-learning/announcing-vertex-ai-prompt-optimizer&lt;/p&gt;
&lt;p&gt;Boost accuracy with the prompt optimizer blog: https://firebase.blog/posts/2026/01/boost-accuracy-with-the-prompt-optimizer&lt;/p&gt;
&lt;p&gt;Connect with us: Let us know what you want us to talk about next!&lt;/p&gt;
&lt;p&gt;#AI #Gemini #AgentSkills #MCP #Flutter #VertexAI #PromptEngineering #TechPodcast&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro: Why we are sitting today.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Hey, welcome to today&apos;s uh techw walk episode, our podcast where we go on a walk and we talk about tech. And today we decided we&apos;re gonna mix it up and do a sit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:08&lt;/strong&gt; · Yeah, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · Yeah. So, I got Rodie.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · Yeah. And I&apos;m with Noey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:13&lt;/strong&gt; · If you don&apos;t know, Rodie does not normally sit next to me, but he comes down on Wednesday nights. Not Wednesday nights, he comes down Wednesdays, and he decides to sit down next to me and tell me a bunch of things I didn&apos;t know. And today, you brought up something called uh skills.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:27&lt;/strong&gt; · Yeah. And I want to talk about that because not knowing skills I think is my own skill issue. How how new how new is this?&lt;/p&gt;
&lt;h3&gt;What are Agent Skills? (Anthropic &amp;amp; Antigravity support) .&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · So agent skills if uh people aren&apos;t familiar with are kind of this new paradigm that um has been popping out in the different tools. Uh Anthropic I believe launched it about a month ago if not a little bit more. Uh anti-gravity just launched support yesterday. And you&apos;re saying I&apos;m a little slow. It just Yeah, we&apos;re we&apos;re a little bit out of the loop but to be fair on holiday breaks. So that&apos;s true. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · Um but one of the cool things about uh skills is uh I don&apos;t know about you but when we were entering the phase last year of trying to make our models and agents smarter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · Um AI rules were the big thing. You know it&apos;s u the agents MD or the Gemini files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · So uh basically ground your project on some type of corpus of information that is very specific and oftentimes including multiple things in one. So, the Gemini CLI has this thing called extensions, which you can install a Gemini.m MD file. Now, you brought up making your agent smarter by having that Gemini.md or that agents.md file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39&lt;/strong&gt; · How, this is where I think I might be a little confused. How often is that file used when you make requests to the Gemini CLI or anti-gravity? Is it used every time or is it only used select times? So with uh when you&apos;re loading your default agents MD or Gemini MD file when you say default uh the one that&apos;s the root level of the project oh the project not not the system okay um at the root level of the project and any recursive Gemini MD files in the&lt;/p&gt;
&lt;h3&gt;Skills vs. Context: The difference between lazy loading and concatenating context files.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:07&lt;/strong&gt; · current project scope it will concatenate all of them and put them into the context of the model when you first launch the AD. If I have a gemini.md file at the root of the project and then I have a source folder and inside the source folder is like a pizza folder where I have all my pizza recipes. I have a Gemini MD file in there, it will it will read both only if it&apos;s working on the pizza recipes, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:26&lt;/strong&gt; · Yeah, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:27&lt;/strong&gt; · But if I&apos;m working in say the ramen recipes, which is a different folder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · Uh this is a terrible analogy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:35&lt;/strong&gt; · Uh but let&apos;s say I&apos;m I&apos;m in like a separate folder then it will not read the pizza recipes. Is that the correct understanding? Uh, I think a different way to think about it is um it will get all of the Gemini MDs for um the tree directory recursively for whatever folder you&apos;re in. So you have my current project and my sub projects and my sub sub projects that it&apos;ll concatenate all of them and you&apos;ll see it when you first launch Gemini CLI or whatever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:59&lt;/strong&gt; · Every request the first time you launch the agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · Oh, okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:03&lt;/strong&gt; · So when you click new chat, it&apos;ll that&apos;s what it will load all the way down to the pizza recipes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:08&lt;/strong&gt; · Oh, okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:09&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:09&lt;/strong&gt; · And of course, uh there&apos;s different harnesses that may tweak this behavior and it&apos;s evolved over time. Um but the downside of this is you had to pay the cost of um being explicit about I want to have these Gemini CLI extensions installed. I want to have these rules files enabled so when I load it into the agent, it&apos;s consistent and at least loading the context every time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:32&lt;/strong&gt; · Yeah. Actually, you know, it&apos;s it&apos;s funny that you say enabling and disabling the the certain extension. So, I installed the Firebase extension obviously. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:41&lt;/strong&gt; · Uh, and I use that all the time because it just keeps loading. I never disable it. Otherwise, I&apos;ll forget to reenable it when I want to use it and I&apos;ll ask it to do things and Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:50&lt;/strong&gt; · So, um, that&apos;s interesting. So, I didn&apos;t necessarily know that every time you started a new chat, it will read all the Gemini Gemini MD files, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:01&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:02&lt;/strong&gt; · Yeah. Okay. So, and then skills builds off of this. Yes. in a unique way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:08&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:08&lt;/strong&gt; · So one important distinction is that skills are progressive on how it finds out about them. So whereas on the Gemini MD files and the other types of agent files where it&apos;s concatenating the resources ahead of time, this is a way for you to say, I have 500 different individual markdown files. I only want you to read the name and descriptions of each of the files so that you know that you have these capabilities and only when you think that that one&apos;s going to be relevant should you read in the rest of the the file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:39&lt;/strong&gt; · Oh, okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:39&lt;/strong&gt; · And so it can be very dynamic per chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:41&lt;/strong&gt; · It so it loads it when it needs it rather than Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:44&lt;/strong&gt; · loads it right at the beginning. You get the whole soup salad and and everything but the kitchen sink. Exactly. Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:49&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:50&lt;/strong&gt; · So when you load these, how does it go about finding which one it it needs? Is it like a tool call? Do you know what the API looks like when it goes to Gemini?&lt;/p&gt;
&lt;h3&gt;How the API discovers and uses Skills.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:59&lt;/strong&gt; · So, how it&apos;s supposed to how it&apos;s supposed to work is uh you have both a global scope for your skills folder and your project scope. And it&apos;s supposed to concatenate them together to say uh when I&apos;m in this project, here are the global tools that I have available and the local tools that I have available. And skills are tools. uh tools as in like they could be available in the skills because a skill could be anything from running a script to um just enabling how to do something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:27&lt;/strong&gt; · So it&apos;s you kind of think of them like tools but they&apos;re really not. It&apos;s more like just in time pieces of context if that makes sense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:34&lt;/strong&gt; · That does make sense. So um like for example like you could say I have a very opinionated way to deploy to cloud run and so you could say um every time you do this make sure you set up a docker compose file and a docker file check for the relevant container and then you know run these commands to build and deploy it to Google cloud and that skill you can install it once and any project you&apos;re in on your your file system will be able to have that ability when you say okay now let&apos;s deploy to cloud run and it says Okay, found a relevant skill. Let me go run the commands.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:06&lt;/strong&gt; · And so it&apos;s like repeatable behavior across your project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:11&lt;/strong&gt; · Okay, I see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:12&lt;/strong&gt; · But scope to that very specific thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:14&lt;/strong&gt; · Gotcha. Gotcha. Okay. Do you know what the skills look like for when it makes that determinist like so so when I say uh what they look like is when you make a request to Gemini, how does how do the skills transpose? Are they just It&apos;s just content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:31&lt;/strong&gt; · Oh, it&apos;s just content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:32&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:32&lt;/strong&gt; · Yeah. So it&apos;ll be a markdown file that it it loads. But the the really amazing part is you&apos;re not having to bloat the context for all the tools you&apos;re not using.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:41&lt;/strong&gt; · Oh, okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:42&lt;/strong&gt; · Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:42&lt;/strong&gt; · Because the model is going to have this discovery phase. It&apos;s not going to have to do all these competing like so for example the Firebase tool, it may have a lot of tools that um are very similar. But this way it&apos;s it only will ever know when to call that MCP tool if there is a use case where you&apos;re dealing with something with Firebase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:02&lt;/strong&gt; · Oh, and then if I&apos;m doing something that&apos;s with Flutter, it&apos;s going to have the Flutter skills that are very specific.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:07&lt;/strong&gt; · So, Gotcha. Very cool. Have you used or built any skills yet?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:12&lt;/strong&gt; · I&apos;ve been uh working on testing them out and uh working with them. Obviously, this is uh still like an emerging standard, but I I&apos;m really excited by the fact that there is um kind of an openness to these. Um there&apos;s already like a a domain you can go to and see all of the uh open skills that are part of the spec. And in fact, we&apos;ll link it in the description.&lt;/p&gt;
&lt;h3&gt;The open ecosystem for sharing and installing Skills.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:31&lt;/strong&gt; · But uh this kind of idea is like how can we across the ecosystem find these reusable bits that uh someone&apos;s learned how to do like through trial and error or pain and be able to make it so anybody can just like add the skill and be able to like have that benefit of doing that task.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:50&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:50&lt;/strong&gt; · And if I recall correctly because I I was trying to read the documentation before we met, but obviously I skill issue. I I I got to a spot where I was looking at how you install the skills and it&apos;s all through git, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:07&lt;/strong&gt; · So it takes like that go model where where you push to a github repo and then that&apos;s like the package and where the package lives, right? So is it the same thing with skills where you push to get and then when you update the skills does it update automatically every time you load or is it is there some sort of So this is kind of one of the differences between this and MCP and some of the other context is one they live in your project. So as opposed to a Gemini CLI extension which it gets installed and updated by a provider.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:37&lt;/strong&gt; · This is saying like most of the skills I&apos;ve seen are a handful of files and maybe a 300 line markdown file if that you know so it&apos;s very very scoped.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:48&lt;/strong&gt; · Sometimes they&apos;re like 100 150 and so you&apos;re going to be putting skills in your project folder that make sense for that project and so other team members as they check out your project would just get the benefit of it. Yeah. But then you may have some global system ones that yes, you probably do want to back those up to get, but it&apos;s totally not required. You could just like throw them up there and every time you interact with them, it&apos;s when it discovers the phase, it&apos;ll have the latest tool because it&apos;s reading it lazily as opposed to eagerly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:17&lt;/strong&gt; · Uh so if I I if I did want to distribute skills, I&apos;d do that through the extensions then, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:23&lt;/strong&gt; · There a lot of people are even distributing them through Google Drive, like through zipping them up. Yeah. It&apos;s just it&apos;s kind of this really wild format.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:31&lt;/strong&gt; · Yeah. Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:32&lt;/strong&gt; · Yeah. I mean uh files work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:34&lt;/strong&gt; · Yeah. It&apos;s true. Like before I did uh you know before I came to work at Google, I worked as a GIS person.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:41&lt;/strong&gt; · Oh, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:42&lt;/strong&gt; · And we would mail each other like we would zip up files and mail them to each other, right? And sometimes it&apos;d be uh reference files. So it would say, &amp;quot;Oh, find uh this shape file at cdocumentariacount.com.&amp;quot; Right. and and and it always got very confusing to ship it, but yeah. Okay. So, just zip it zip it and ship.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:02&lt;/strong&gt; · Maybe we can uh FTP our skills together, you know, over an NAS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:06&lt;/strong&gt; · Yeah. Yeah. Yeah. And then, you know, have it get hacked. Yeah. Yeah. We had that conversation earlier today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:12&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:12&lt;/strong&gt; · Uh what&apos;s interesting is you brought up how it doesn&apos;t overfill the context. And recently, one thing that I&apos;ve been working on, uh it&apos;s coming out next week. All right. which it&apos;ll probably be live by the time this is live, right? The magic of editing. Uh is is uh I don&apos;t know if you met Elena, but me and Elena worked on a blog post together.&lt;/p&gt;
&lt;h3&gt;Deep Dive: Using Vertex AI Prompt Optimizer for YouTube descriptions.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;10:33&lt;/strong&gt; · Oh, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:33&lt;/strong&gt; · For the Vertex AI prompt optimizer. I don&apos;t remember if we had already talked about this, but I&apos;m going to talk about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:39&lt;/strong&gt; · So, so what is the prompt optimizer? So uh the prompt optimizer it is a way to take your prompt right your singleshot prompt and optimize it into a very uh good prompt right so for instance uh you know this we we are working on YouTube descriptions right and we had a marketing person write our descriptions for years and they would get our scripts they&apos;d watch the video they&apos;d go through they would type out everybody&apos;s name and give us a draft that we then still edit on obviously, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:11&lt;/strong&gt; · We&apos;d still edit it, we&apos;d add chapters, we would do these things, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:16&lt;/strong&gt; · Just meaning it wasn&apos;t hands-off like we were still very much involved. Yes. Yes. Yes. So, one thing that I found is like could we save this marketing person all this time to go work on like more impactful things by optimizing a prompt for Gemini, right? So, I tried writing my first prompt for the prompt optimizer and it came out uh it came out so weird and bad.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:41&lt;/strong&gt; · Oh, no.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:41&lt;/strong&gt; · Like, right. So, like I gave it my script and it gave me a result back and I was like, well, all right. I guess this is all right. And like my prompt wasn&apos;t like complicated, right? It was just like write me a YouTube description that is SEO optimized for fire the Firebase channel, the big tech company, right? And uh it was it was bad. And it was it wasn&apos;t only just bad the first time I did it. It was bad. It was it would like vary every single time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:10&lt;/strong&gt; · Continue to be bad. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:11&lt;/strong&gt; · So it was it was bad in a unique and different way, right? Every time I ran it. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:15&lt;/strong&gt; · Which is like the beauty of non-determinism. So uh I I I I was I was lamenting about this to to Dan and Elena.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:24&lt;/strong&gt; · And uh Dan, I believe suggested I look into the Vert.ex AI prompt optimizer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:29&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:29&lt;/strong&gt; · So now what you do is it&apos;s uh they have two. They have a zero shot which is what I did and or what I started with, right, is I just say like help me write a good YouTube description and it&apos;ll take that and it&apos;ll give it to Gemini and Gemini will like power up your prompt to make it better to use for Gemini.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:46&lt;/strong&gt; · So then you get like a better which model like does it does it pick one or do you like decide that you know it&apos;s been so long since I did this right before break and then I wrote the I wrote the blog before break and then I just put like the final touches on it. I don&apos;t remember. I think you can pick between flash and pro and all that, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:02&lt;/strong&gt; · But generally like a family of models like just five or three.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:04&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:04&lt;/strong&gt; · Yeah. Exactly. So, uh the zero shot one it it gave me a really good result, but I we have years of of scripts and the script, right? And everything is documented. Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:16&lt;/strong&gt; · It&apos;s not just like I go in front of a camera and talk for 40 minutes and we have the we have the script and the metadata attached and like the video itself and the transcript.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:24&lt;/strong&gt; · We have it all right there. Right. So what I did was uh I put all that into a Google sheet.&lt;/p&gt;
&lt;h3&gt;Converting CSVs to JSONL for data-driven optimization.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;13:29&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:29&lt;/strong&gt; · I exported it to a CSV. I as Gemini CLI to convert it to a JSON L file because I think it&apos;s required. It might be it might work with CSV.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:38&lt;/strong&gt; · And what is JSONL?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:39&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:39&lt;/strong&gt; · Uh JSONL is uh JSON where everything is on one line per record.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:46&lt;/strong&gt; · And then uh so if you have like an array of like 15 different records, it just puts everything on one line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:52&lt;/strong&gt; · Okay, that&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:52&lt;/strong&gt; · Yeah, probably easier to parse. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:54&lt;/strong&gt; · Yeah, exactly. Uh so I I collected uh I think it was like 50 45 to 50 scripts and 45 to 50 descriptions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:03&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:04&lt;/strong&gt; · Got it all formatted all that and then I let it run. And what I learned is uh after about 20 minutes you&apos;re convinced that it&apos;s broken, right? Because you don&apos;t get any like feedback. Like you go through, you set the settings and then you click like go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:19&lt;/strong&gt; · It&apos;s a collab uh notebook, right? and you and you hit go and you sit there and you&apos;re like, I&apos;m sure it&apos;ll be fine, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:25&lt;/strong&gt; · I feel like uh that was me with like early iOS development when you&apos;re waiting for Xcode to build or Android Studio and you&apos;re just like, I guess I&apos;ll go get coffee.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:33&lt;/strong&gt; · Yeah, I guess I&apos;ll I&apos;ll do this, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:35&lt;/strong&gt; · Well, it was like after about 20 minutes, uh I was convinced that it was broken, but I was like, you know what?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:39&lt;/strong&gt; · It still says it&apos;s doing something. I&apos;m going to let it churn. I&apos;m going to go get coffee. I&apos;m going to go get lunch. And about five hours later, four to five hours later, uh, I had this really optimized prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:52&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:52&lt;/strong&gt; · Right. And it was much longer than I had originally written. Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:56&lt;/strong&gt; · So, you think it&apos;d be shorter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:58&lt;/strong&gt; · My first prompt was like maybe two lines, right? Three lines. This this had details on chapters and how to recognize the chapters. It had It will say, &amp;quot;Oh, hi. I&apos;m Peter.&amp;quot; Right? It&apos;ll say the speaker, if they say their name is Peter, it&apos;s actually Peter Freeze. If it&apos;s Frank Pufflin, it gave a list of, you know, how you put tags in YouTube videos. It gave a list of tags and uh just talking about token consumption. Do you want to take a guess at how many tokens I used?&lt;/p&gt;
&lt;h3&gt;The 100 Million Token experiment.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;15:26&lt;/strong&gt; · So, this is like tokens used in training.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:30&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:30&lt;/strong&gt; · Because it what it does is it it does this continuous improvement loop where it will run the prompt, take the output, and then run an eval against the uh the ground truth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:41&lt;/strong&gt; · Okay, got it. So, I would say for a It&apos;s hard to know how much concurrent it was doing, but maybe a couple million tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:50&lt;/strong&gt; · What What&apos;s a couple million to you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:52&lt;/strong&gt; · Uh, yeah. Give me like a hard number because you&apos;re a couple million 7 million tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:58&lt;/strong&gt; · That&apos;s it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:59&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:59&lt;/strong&gt; · That&apos;s your guess.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:00&lt;/strong&gt; · Oh, I guess it Okay. Upper end maybe 30 million.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:06&lt;/strong&gt; · Okay. It took about 100 million tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:08&lt;/strong&gt; · Oh, wow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:08&lt;/strong&gt; · Yeah, it was it was like uh it was a very large Yeah. numbered tokens, but like the prompt works really well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:17&lt;/strong&gt; · And I mean, does it do something similar to like ML where it has like some kind of a fit model and it&apos;s like kind of doing some kind of a training and working its way back?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:26&lt;/strong&gt; · So, I&apos;m not entirely sure on that because you you run this in in a notebook that was designed by Elena&apos;s team and fair.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:33&lt;/strong&gt; · It runs in kind of like a little bit of a black box, but the black box, you know, does some Okay. it like this training and and and there&apos;s some sort of uh execution that verifies the results because I know that in Google Cloud we do have an option to train ML models on tabular data and I&apos;m curious if it might be like a similar technique probably.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:53&lt;/strong&gt; · That&apos;s awesome though. I mean, it&apos;s really cool that like one, it was able to be consistent and from there, that&apos;s really where system instructions can help you really fine-tune the final experience because like a lot of people don&apos;t realize, but you know, that&apos;s why the prompt engineering and context engineering really is so important because it&apos;s it&apos;s not just that you&apos;re wanting to get a better result. It&apos;s you&apos;re you&apos;re activating the specific weights that you want to have, guiding it to the things you don&apos;t want, and to be at least consistent on something that you can parse out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:26&lt;/strong&gt; · Yeah. Yeah. And it it it was I&apos;m I&apos;m just actually like so happy with the results. And you know, it it&apos;s 100 million tokens is a lot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:36&lt;/strong&gt; · That is a lot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:37&lt;/strong&gt; · It&apos;s a lot for like a single operation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:39&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:40&lt;/strong&gt; · But here&apos;s the flip side of it. We just saved at least that much money of that person&apos;s time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:46&lt;/strong&gt; · That&apos;s fair. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:47&lt;/strong&gt; · Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:47&lt;/strong&gt; · And and it&apos;s not that they cost, you know, even if it didn&apos;t cost them that much to do those types of things, they get to go use their time on more expensive, harder, more complex tasks than reviewing my scripts. And and maybe also saving time from a just try to get Gemini to generate a response or like a like a result and then trying to work it to something that&apos;s usable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:10&lt;/strong&gt; · Oh yeah. like, you know, this is like getting them closer to that final result a lot faster. So, even if they do have to do some passes, it&apos;s still much more minimal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:18&lt;/strong&gt; · Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:19&lt;/strong&gt; · Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:20&lt;/strong&gt; · That is really cool. Yeah. So, I mean I mean people should try it out if they they have a prompt that you know is really uh specific.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:28&lt;/strong&gt; · I wouldn&apos;t encourage people to just go try it out, right? I think you gota if you have a a task that is highly repeatable cool that uh you want to minimize the human interference or human in the loop where you want to get to a more deterministic output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:43&lt;/strong&gt; · So what about something like ADK? Would that be something that would be a good use case for the prompt optimizer?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:48&lt;/strong&gt; · It yeah I mean it really depends if you&apos;re running uh the prompts a lot right and and and I mean like a lot like multiple times a day or multiple times a week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:58&lt;/strong&gt; · Minimal intervention. Yeah. minimal intervention, right? This would be a good prompt to optimize for ADK in these workflow agents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:05&lt;/strong&gt; · So, where would someone go to get started with it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:07&lt;/strong&gt; · Google search for Vert.ex AI uh prompt optimizer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:10&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:11&lt;/strong&gt; · And uh I use the data driven one. They also have a zero shot one. They&apos;re both in collab notebooks. Um we&apos;ll make sure to link them in the description.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:18&lt;/strong&gt; · Rodie will make sure to link them in the description.&lt;/p&gt;
&lt;h3&gt;Exploring MCP (Model Context Protocol) with Flutter.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;19:21&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:21&lt;/strong&gt; · Brody, what happens is uh when I&apos;m at work, I&apos;m working and then you&apos;ll come in and you&apos;ll sit down next to me and you&apos;ll tell me things that I&apos;m half listening to and I&apos;m trying to do both. And today you brought up something about MCPs in the Flutters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:38&lt;/strong&gt; · Yes. So, uh, one of the projects I&apos;ve been experimenting with is, um, building with MCP. You know, we both have exploring MCP tools in a variety of capacities forever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:48&lt;/strong&gt; · Yeah, I know. And I feel like sometimes they get a bad rap because we&apos;re using them maybe excessively or maybe in the wrong context. Like you load it up into your coding agent, you have like 150 tools and the model&apos;s having a hard time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:02&lt;/strong&gt; · But you know, I think that there&apos;s this awesome opportunity to be able to support using MCP for maybe applications like in the client side. So uh one of the things I wanted to try out is um take a Flutter app for example like a desktop or mobile. I could expose the functionality that I want to to other applications as an MCP server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:23&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:23&lt;/strong&gt; · And so, um, mobile apps are kind of unique and desktop apps in that sometimes you have bought a license key or you&apos;re on an inapp purchase or you have a subscription. So, you might have more or less tools available depending on that authenticated state or the capabilities of the device for example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:42&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:42&lt;/strong&gt; · Um, and MCP is really cool for that because you have to pull the tools that are available at any given time and then to be able to call back with the exact schema. Um, and so what I did is I took the counter app that everybody loves and I MCPified it. So I uh I exposed all the counter state like set value, get value, increment and decrement and I could just copy the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:05&lt;/strong&gt; · config and paste it into anti-gravity as a local HTTP server and I could say set the count to a million and it would talk to the running app, set the state and then be able to return back to the So you had an MCP server written in Dart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:18&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:19&lt;/strong&gt; · That then exposed tools over HTTP or or JSON RPC or whatever they Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:26&lt;/strong&gt; · Okay. and not through the IO me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:29&lt;/strong&gt; · Correct. It wasn&apos;t using the Flutter MCP server. It was actually like the Dart application itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:34&lt;/strong&gt; · Okay. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:34&lt;/strong&gt; · So like and and the application had to be running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:37&lt;/strong&gt; · The application had to be running. And so one of the cool things that that unlocks is one, you have this ability to have a running app and say maybe if you have your favorite editor that you want to use or maybe another application that can also do tools, you can combine them together and then now this this coding agent can control all your applications, you know, so like you can expose like really cool functionality. But then I had a day to sleep on it and I was like, I bet I can get this working over uh standard Unix sockets and I and I got it working and it was so cool.&lt;/p&gt;
&lt;h3&gt;Using Unix Sockets vs. HTTP for local app control.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;22:10&lt;/strong&gt; · That was my next question was cuz there&apos;s like the IO&apos;s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:13&lt;/strong&gt; · Yeah. Yeah. Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:14&lt;/strong&gt; · And so then I could query like the get the documents directory. I could get a path on the file system, open up a a Unix socket and it would work the same on Windows with pipes. But it just worked like it worked so well and I was so excited because one it didn&apos;t need any dependencies and I was using anti-gravity to build it but it&apos;s like I started getting excited about this world where like what if apps like have IPC&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:38&lt;/strong&gt; · between themselves but it&apos;s all dynamic right cuz like I mean when I was doing a lot of iOS and Android development um sometimes you get like callback schemes or the ability to like push data out but it was very rigid you know like your app might be installed, they may forget to update this app. It&apos;s always like which version are you have and it&apos;s like always uh let&apos;s just add a new API as opposed to like change it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:00&lt;/strong&gt; · And so I feel like this allows you to say no like it is dynamic like it can change at any time like I could deprecate functionality and that&apos;s why you have to pull for whatever tools are available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:12&lt;/strong&gt; · Gotcha. Okay, that&apos;s cool. Yeah. You know I So is your next step to run it as like a Damon service on device?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:20&lt;/strong&gt; · So it already is like it works. Yeah. Yeah. Yeah. So, like basically when you start up, you can say if you want the HTTP server or standard IO and it&apos;ll just start the socket and be listening and then you can like shut down the app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:30&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:31&lt;/strong&gt; · Oh, that&apos;s very cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:32&lt;/strong&gt; · Yeah. So, you could run it headless. Um, but the app does need to be running in some capacity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:36&lt;/strong&gt; · Gotcha. Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:37&lt;/strong&gt; · Yeah. And and that&apos;s that&apos;s interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:39&lt;/strong&gt; · You know, at Ash and I earlier this year, we we tried building uh an MCP client that hooked into Firebase AI logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:48&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:49&lt;/strong&gt; · On Android.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:50&lt;/strong&gt; · Okay. On device. Yeah. one device, right? But just a client, not a server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:53&lt;/strong&gt; · Okay. Right. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:54&lt;/strong&gt; · And uh that was I I find that the MCP spec has so many different variables to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:01&lt;/strong&gt; · There&apos;s multiple ways to interpret this the spec, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:06&lt;/strong&gt; · Yes. Yes. And it&apos;s pretty exhaustive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:08&lt;/strong&gt; · And I was only touching the JSON RCP part.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:10&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:11&lt;/strong&gt; · Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:11&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:11&lt;/strong&gt; · Because I I I was using the Astro spec I tested on and then Ash went to go test it as well and she&apos;s like, &amp;quot;It doesn&apos;t work.&amp;quot; I was like, &amp;quot;What do you mean? Did you try it with Astro?&amp;quot; like, &amp;quot;Yeah, but it doesn&apos;t work with this other MCP server that somebody else built and they conflict.&amp;quot; Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:24&lt;/strong&gt; · Yeah. Yeah.&lt;/p&gt;
&lt;h3&gt;Implementing the MCP spec in Antigravity.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;24:25&lt;/strong&gt; · Uh, have you tried building a server and like matching to a spec or anything?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:29&lt;/strong&gt; · Um, basically a limited spec. I just wanted to support tool calling and um, I was able to like fully implement the spec um, pretty easily with anti-gravity. And what I did is I basically created like an abstract class that had all the methods and the handlers for dealing with the JSON bytes. And then it was super easy to extend it to be a HTTP server or a Unix socket.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:51&lt;/strong&gt; · Nice. Cool. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:53&lt;/strong&gt; · Well, I hope we can hear like more next time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:56&lt;/strong&gt; · Yeah. We&apos;ll see if it evolves some more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:57&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:57&lt;/strong&gt; · Yeah. Cool. Well, uh, thanks for having me on your podcast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:03&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:03&lt;/strong&gt; · No, it&apos;s it&apos;s cool to like, you know, change it up and and sit. But, uh, yeah, there&apos;s a lot of cool things that I feel like we&apos;re both working on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:10&lt;/strong&gt; · Anything that you&apos;re excited about, uh, this week?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:13&lt;/strong&gt; · Uh, this week? No. No. Uh I&apos;m I&apos;m just trying to get like my blogs out the door, right? And and I&apos;m sure we&apos;ll link them below. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:20&lt;/strong&gt; · Uh whenever this drops probably everything will probably be I&apos;ll probably have something new by the time this drops.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:25&lt;/strong&gt; · Fair.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:25&lt;/strong&gt; · So uh nothing to tease yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:28&lt;/strong&gt; · Nice. Nice. Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:29&lt;/strong&gt; · What about you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:30&lt;/strong&gt; · Yeah, for me I uh kind of exploring some different um AI things that cloud has to offer right now. Seeing how they have too many. I know. It&apos;s so hard to enumerate all of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:39&lt;/strong&gt; · I feel like today I was testing out a Python notebook and doing all the things. So, it&apos;s out of my comfort zone, but I&apos;m pretty excited. So, yeah. Yeah. We&apos;ll have a lot of fun for our next podcast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:48&lt;/strong&gt; · Absolutely. Yeah. Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:49&lt;/strong&gt; · All right. Um, we&apos;ll uh link everything in the description in case you missed it. Um, definitely make sure to let us know what you want us to talk about next. And we&apos;ll see you next time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:57&lt;/strong&gt; · At next or next time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:59&lt;/strong&gt; · We&apos;ll see. Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:00&lt;/strong&gt; · Oh, that&apos;d be fun. A Vegas show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:02&lt;/strong&gt; · That would be fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:03&lt;/strong&gt; · Yeah. Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:04&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:04&lt;/strong&gt; · Yeah. Peace. Toodles.&lt;/p&gt;
</content:encoded></item><item><title>Building the Flutter Widget Maker and Storyboard</title><link>https://rodydavis.com/podcast/creative-engineering/building-the-flutter-widget-maker-and-storyboard/</link><guid isPermaLink="true">https://rodydavis.com/podcast/creative-engineering/building-the-flutter-widget-maker-and-storyboard/</guid><description>This technical discussion explores building Flutter widget makers and storyboards, featuring an in-depth look at how GitHub Code Spaces enables developers to conduct online coding and testing for Flutter and various languages using containerized environments.</description><pubDate>Tue, 14 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Building the Flutter Widget Maker and Storyboard&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;Edvo_MSrzrg&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Norbert Kozsir - @norbertkozsir&lt;/p&gt;
&lt;p&gt;https://twitter.com/norbertkozsir&lt;/p&gt;
&lt;p&gt;https://github.com/norbert515&lt;/p&gt;
&lt;p&gt;Rody Davis - @rodydavis&lt;/p&gt;
&lt;p&gt;https://twitter.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://github.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://youtube.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://rodydavis.com&lt;/p&gt;
&lt;p&gt;Our podcast player:&lt;/p&gt;
&lt;p&gt;https://rodydavis.github.io/creative_engineering/&lt;/p&gt;
&lt;p&gt;Follow on Twitter:&lt;/p&gt;
&lt;p&gt;https://twitter.com/CreativeEngShow&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · welcome to creative engineering today we&apos;re going to be talking about widget Builders flutter editors idees analyzer plugins storyboarding and much more with me I have my co-host Norbert how are you doing doing pretty well um it&apos;s been a week where I finally um got back into&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:18&lt;/strong&gt; · publishing something um actually two things which we&apos;ll talk about in a minute how have you been doing doing pretty good yeah uh just been pretty crazy uh have been releasing a lot of plugins to um speaking of some announcements that&apos;s come out GitHub announced code spaces pretty crazy have you heard of it yes I&apos;ve seen it of course they they own vs code and now&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · they all they also own GitHub and now they combine those two pretty cool things into one and like I&apos;m just thinking about the possibilities of not having to clone a repository go in there do some changes to a PR just just being able to open it online changing something and just having it all in one place looks like a big thing yeah what&apos;s cool about it too is they&apos;re offering you know a Docker based um environment&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11&lt;/strong&gt; · so you can have flutter installed they have their examples they have node.js rust and typescript and just the the ability to open up something that doesn&apos;t require UI like there&apos;s many times I may fix like a bug that&apos;s just in Dart and it&apos;s just a logic bug and you basically want to able to just write something and then run your test and this is something that you can do right from GitHub now which I think is super super cool I mean think about how many times we want to just like release a plug-in update and you know we have to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · either do it from our local machine just to submit it to Pub or you know just these other crazy hacks we come up with but what&apos;s cool now is like you could actually log in to Pub from the terminal from the shell here and then actually publish your plugin uh without actually needing to download it which I don&apos;t know I don&apos;t know about you but like for me I have started to run out of space because of all the repositories on my machine I recently had to like go and clean up a bunch cuz I mean it starts to add up like when you start forking a bunch and doing all that yes I I literally sat down I think was yesterday&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · when uh my disc space was running very low like low as zero bytes on C uh which Isn&apos;t that cool and I went into my workspace floor and there were so many projects which with each being over 1 gigabyte and in the end it turned out I had all the old Desktop Windows ands in there which were all taking up like 600 Megs each so I was just able to delete the old ones because you&apos;d have to recreate them if you run this with the new version so that saved me like 10 GB&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · uh which was pretty nice yeah I actually rained the same thing it was all the runner builds for Mac OS that are taking up so much space and I guess it counts a new build every time yeah because it would be a a new archive build but yeah that was really annoying because I think I cleared up like 75 gigs of all the the code that taken off yeah it was a bunch&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:09&lt;/strong&gt; · cuz like you know each repo maybe be over a couple gigs because of the changes and the content inside of them because I&apos;m like storing images too and yeah it was a lot now I feel my 10 gigs is nothing compared to your 75 yeah also have this like weird bug I hope other people aren&apos;t having this too but basically the dart analyzer will start picking up like 100 plus gigs of memory and then yeah it&apos;s weird like I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:35&lt;/strong&gt; · can restart I I don&apos;t know I&apos;ve had this happen on a couple different computers of mine but like Mac OS will like the system will start taking up like 300 gigs 200 gigs and then I&apos;ll restart my computer and then it goes away and it&apos;s like super annoying I don&apos;t have that much memory but so I&apos;ve been so my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:53&lt;/strong&gt; · normal day-to-day storage space like 10 gigs or five gigs on C which uh my laptop&apos;s a bit old um but yeah I&apos;ve noticed the same thing like I&apos;m not sure where it&apos;s because of the door analyzer but over the day it fills up at some point Windows just notifies me okay you have no more space left and at this point I restart my computer and it&apos;s all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:17&lt;/strong&gt; · back like do you think it&apos;s because of D analyzer yeah I think maybe it could be like it&apos;s not garbage collecting correctly or maybe it&apos;s uh it&apos;s something to do with like the pub cach cuz I can rebuild the pub cash and it like fixes most of the issues like I have a couple projects right now that just won&apos;t analyze like it&apos;ll it&apos;ll get stuck on you know processing and then I&apos;ll have to restart and then usually that&apos;ll fix it even yeah it&apos;s there&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:41&lt;/strong&gt; · just some weird issues I mean every now and then I feel like I&apos;ve had these analyzer issues and then there&apos;ll be a fix and then it&apos;ll break again in a couple weeks so I don&apos;t know I don&apos;t know if hopefully it&apos;s just my machine and not actually something everybody else is facing too nice weird indeed yep&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:58&lt;/strong&gt; · so uh today like we said we&apos;re going to be talking about you know UI Builders and editors and um just trying to have a discussion about you know like what the current state is so um based on my knowledge I feel like there&apos;s you know a couple players out there there&apos;s Adobe XD which has some Community plugins that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:15&lt;/strong&gt; · export to flutter um I know that they&apos;re officially releasing their plug-in that&apos;s going to go to flutter projects but that&apos;s not out yet there&apos;s been Supernova which is really cool and flutter interact they announced some really cool updates with it um that one actually allows you to open up a wxd and sketch and Export flutter projects which is super cool um and just as a side note that one of my favorite editors that I had found which never actually got to use which was the dart editor um but I thought that was really cool cuz that was a whole editor that um the dart team&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:47&lt;/strong&gt; · released about the same time as darum allowing you to edit Dart code um without actually having to have Dart installed and of course we have dartpad um and code pin for that matter of online editors that you can build individual screens with code in a declarative way um am I missing any we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:05&lt;/strong&gt; · both don&apos;t know what the status is but on FL interact Google itself uh um mentioned a UI edor Builder uh which they were working on they have been very secretive about it like I have I&apos;ve looked online and there&apos;s no trace of this ever being like there is the video of it being uh announced and spoken about but nothing else it&apos;s like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:30&lt;/strong&gt; · ghost like yeah you can search it on Google and you won&apos;t find it they even provided more information about Fusia than this yes I know yeah I I had the same concept you know I was like I was searching everywhere I was like maybe I just missed it maybe I&apos;m typing the wrong name it&apos;s like nope it&apos;s just like not there so either one they backed out&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:50&lt;/strong&gt; · on it or two they&apos;re just not ready uh we&apos;ll see but um yeah I mean and then also too I just wanted to talk about you know cuz you you&apos;ve had your widget maker for a while and um just uh wanted to talk about some of the things that you faced when building it and um the fact it&apos;s open source now which uh we&apos;ll definitely link in the description but uh so what um what made you want to build the widget maker so um I think the story started I was just bored and I thought okay how cool would it be if you could open the Flor project in itself&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:23&lt;/strong&gt; · like imagine just running an app on your phone and just opening the source code of that app inside that app and reload itself so I start with this uh weird and crazy experiment of building this little so-called phone IDE project which had the most terrible UI you probably have ever seen uh but it was a pretty neat proof concept uh basically what was happening uh on the phone it uh got the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:51&lt;/strong&gt; · files via an HTTP server on the computer which it was connected to and when it wanted to hot reload itself it basically sent a request the computer and it was connected to the computer via ADB Wireless uh and so the computer was able to just hot reload it over the the Wi-Fi and just have this magic effect of having your complete app on the phone modifying the app itself on the phone which was so interesting that I just uh kept exploring the idea of trying to to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · make widgets and make the building of UI um more approachable and more efficient and like the current version which is online is actually I think the third iteration like I&apos;ve Rewritten the code at least one or two times because the ideas I had and all these stuff that was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:46&lt;/strong&gt; · floating around in my head constantly kept changing and I tried to uh figure out what I actually wanted to build yeah so like um what approaches did you take when first building the the UI part of it because I feel like when you&apos;re working with flutter widgets you know there&apos;s so many different types of widgets that flutter provides like could pretty pretty hard to like you know have that um strongly typed because there&apos;s no like receive a list of all the widgets and all the properties you have to do that kind of manually um like I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:17&lt;/strong&gt; · tried um a couple of things like as you said there&apos;s no way to get all the widgets easily um you what you can actually do is just using Dart analyzer uh analyze all of FL itself and just see which object extends for example play stateless state for widget or something like this but uh I actively decided&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:40&lt;/strong&gt; · against using this because when you scan all those widgets it doesn&apos;t make sense to handle a scaffold the same way you would handle list view you need special UI to to allow the user who&apos;s building the app to interact with the list view differently than the scaffold like you as a developer who&apos;s building uh using this uib don&apos;t want one generic widget which doesn&apos;t work very very well for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:08&lt;/strong&gt; · all the widgets you want those widgets to work well with the individual components so what I ended up doing is mostly handmade like rep each widget at some point a lot of widgets were pretty basic like a center and a line it&apos;s just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:24&lt;/strong&gt; · a widget which has a child and takes it for properties um I actually buil a code generator uh where I was able to just specify a few properties and generate all this code which I think struck a pretty good balance between uh generating stuff but in other hand also having the control to then actually go in there and say okay for the Align I maybe want to allow the user to actually drag the widget inside the editor like instead of uh typing a line uh bottom&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:52&lt;/strong&gt; · left you just drag the widget which is in the line to the bottom left corner and it changes that like with with this approach you can&apos;t have these special UI features in there without having to deeply modify the code which you&apos;ve been doing did you face any issues when you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:08&lt;/strong&gt; · know having it all you know combined together cuz I feel like um when I first um started building this like you don&apos;t think about you know well how do I show a column that has no children or a row that has no children like you have to like you know make guesses about what the user is trying to do and then like you said in a list view for example like if you just add a list View like by default it&apos;s going to throw an error so you have to like basically add a default kind of state and then allow it to be edited yes like as I said there&apos;s a lot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:38&lt;/strong&gt; · of components which need this uh this special attention for list views uh for or for columns and rows in particular um so what I&apos;m doing right now but which isn&apos;t perfect like you have to think about what what you want to do is for example if it&apos;s empty it tries to be as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:55&lt;/strong&gt; · big as possible and hint to a user that this container can put something in if it has some kind of childel it tries to be as small as possible and just show the spaces like before the first widget or after that one where you can drop in the next widget um but like as you said&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:12&lt;/strong&gt; · you have to make guesses and try to think about what what would I want or what would I expect um but yeah like that&apos;s definitely a thing you have to you have to talk to people you have to try out you have to um see what works and what doesn&apos;t um but yeah like there&apos;s a lot of different uh widgets which need special care and that&apos;s also one of the reasons I uh finally decided to open source it like those widgets could be uh like people could make one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:41&lt;/strong&gt; · separate widget and make a good ux and UI for that uh without having to touch most of the the other parts of the code and with that the ability to collaborate and this is uh pretty big because you say okay I made this very nice component for examp for the Align I implemented dragging the Alli which is in there um so yeah that&apos;s one thing you have to to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:08&lt;/strong&gt; · watch out because you want to provide a good user experience for each of the components like it doesn&apos;t make sense to just throw it out there and well supporting multiple widgets um doesn&apos;t work that way um have fun it&apos;s more like have less components to support maybe have a uh smaller use case smaller uh uh feature list but then have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:34&lt;/strong&gt; · those features which are in there work very well because then you can say okay maybe don&apos;t use this for a full-blown production app but if you want to have this little screen sure go ahead try it out build it should work pretty well yeah for me I&apos;m I&apos;m trying to support all the material design and not touching slivers ortino cuz those are two other&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:54&lt;/strong&gt; · huge crazy things and also you know I was going to say you know one thing thing you may not think about when building something like this is the code that you write normally is not necessarily the code that you would expect to have written from these these generators because you know to get something to look correct it may look way different than you would have you know written out in a declarative way you just may take a couple lines to write something that you know is just it&apos;s way different like so like when you start adding stacks and um you know just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:25&lt;/strong&gt; · all these various things when you want free floating widgets and stuff like that it just you got to think about how it all fits together and um yeah it&apos;s just when you have a UI Builder it&apos;s necessarily going to be different for sure yeah and it&apos;s like also super hard to like um compared to Android for example or the web where you have this um static representation of UI um the Android XML stuff uh the web HTML and CSS things in in Flor you have code and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:55&lt;/strong&gt; · code itself is it&apos;s breathing it&apos;s a like you can do all sorts of weird cool practical and smart things and first off uh building something like this and then reading it back in and just processing it uh that&apos;s like a big challenge which um yeah yeah which I I&apos;ve tried uh I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:20&lt;/strong&gt; · played around with a lot of ideas uh even as far as building an own programming language which targets the format uh which which I have done it works uh it&apos;s been a fun project maybe it&apos;s going to see the light of the day at some point um yeah maybe but um in the end this is this is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:42&lt;/strong&gt; · something super hard and you have to to have compromises like you can say okay the code which is generated has this uh um style uh which is good code like you can use it maybe it&apos;s a bit different from what you&apos;ve been would have been writing but it&apos;s also not Cod which you don&apos;t want to touch ever yourself like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:02&lt;/strong&gt; · just generate it forget about it that also um doesn&apos;t sound like a good way to to approach this because what if you want to use it for a widget but then you want to add stuff on top which isn&apos;t support right now you would have to rewrite it if the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:19&lt;/strong&gt; · code is bad yeah totally and like you know people have asked me like why why can&apos;t you just like support my existing widgets and it&apos;s like I can compile your widgets but the problem is you know like you can have build methods that um or return methods that actually return widgets and then you those can depend on other files those can depend on logic that doesn&apos;t exist in the runtime you a widget can be presented in a way that is different from the runtime like so you have like a a UI building mode and then an editor building mode and so yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:50&lt;/strong&gt; · that&apos;s been quite challenging and then also the fact is like I made the um you know a distinction when I started building mine that any widget created is immutable so like you know you can use the widget at runtime but once you use it like you can&apos;t really modify it which&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:06&lt;/strong&gt; · is on purpose because once you start modifying it then you got to figure out like what they changed and how they changed it versus like um using a generated widget approach just like you wouldn&apos;t touch generated code and dart I&apos;m trying to do the same thing with a generated UI code so then you can just you take advantage of the widget but um that way I can always parse it so are you talking about the code which is generated as text or the widget which is running in an editor where you press buttons where you navigate where you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:37&lt;/strong&gt; · yeah both they&apos;re both stateless so they&apos;re all stateless So like um when you when you actually click export it&apos;ll generate the files and a generated Dart file and then the editor also saves the state of where all the widgets are and the locations to them and stuff like that um that way when you go back and open the editor you can have your project again instead of having to lose all your work m i see you yeah that&apos;s one thing um which I currently don&apos;t&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:03&lt;/strong&gt; · support like going the other way around exporting it and importing back in um it&apos;s been a thing I&apos;ve been taking a look at and that&apos;s that&apos;s the thing like what&apos;s the the safe format the most natural thing would be and is to save it as code but then you have this problem that reimporting it is not that easy if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:22&lt;/strong&gt; · you modify it in some unexpected behav uh unexpected way I mean you don&apos;t want to save it as some sort of Json and XML because then it loses the the power of what widget actually is like it&apos;s code right so for me I I save it in two different ways I I&apos;m using the freeze package to create my mutable data structures with all the code generated um stuff and I have a top level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:47&lt;/strong&gt; · Json or I can also use binary but I&apos;m using Json right now to store the project settings the widget configurations like the abstract configurations and then from there that&apos;s used to generate the darkart code so you have real dark code in the locations that where you&apos;re using it but then you also have the ability to customize it and reimport the editor but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:10&lt;/strong&gt; · so if you then want to customize the actual dark code which is generated you can go back into that right like I mean I mean it&apos;s good and it it serves its purpose but like I&apos;m thinking about a way or a word where this would be possible where you could do all the modification you wanted but like you need some sort of like if you&apos;re just trying to patch edge cases like okay maybe don&apos;t Define setic functions it&apos;s not going to work because there&apos;s an endless amount of edge cases um like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:41&lt;/strong&gt; · literally endless uh the grammar for this is uh I don&apos;t know how it was called but there&apos;s just endless ways to do something yeah um exactly so so I&apos;m not sure what the best way would be to approach something in a very Universal way um but yeah in the meantime I just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:02&lt;/strong&gt; · said okay you built the widget you exported it&apos;s yours um being able to then repport it using these this basically what it is it&apos;s a safe file for the project right yeah well I&apos;ve also been um working on building my own dart parser like a very crude parser so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:19&lt;/strong&gt; · like for the chances that you&apos;re only and I only support if you&apos;re only importing material or Dart libraries so if you&apos;re importing anything that&apos;s not Dart or like a third party package this doesn&apos;t work or even another file but basically what I&apos;m able to do is I&apos;m able to parse down find all the classes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:36&lt;/strong&gt; · find where the build method is and then I&apos;m working on actually creating my own like analyzer to like in a very quick efficient way parse a dart file and get the outline of like the widgets so I can say scaffold with a child of this with a child of this with Children of this and um that way I can easily import stuff because like once you have that you can you know build a crude import but I&apos;m also working on there&apos;s a really cool package called reflectable which if you run it at runtime it will give you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:10&lt;/strong&gt; · somewhat um similar Behavior as Dart mirrors allowing you to kind of like introspect all the things that you&apos;re using I actually use this to get all the properties for the widgets that I support so um I can like import from Material scaffold and get all the list of the Constructor items and then um I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:29&lt;/strong&gt; · think I showed you a demo but basically you could use the same approach for existing widgets too um but the problem is this needs to run ahead before you actually import so yeah it&apos;s just it&apos;s interesting problems it&apos;s using Code generation right do you ref reflect yeah yeah like um that&apos;s also the thing uh I looked at with my custom language um so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:53&lt;/strong&gt; · basically I&apos;ve been following this book by um don&apos;t want to mispronounce his name Bob neist he works on the uh dart team with uh the dart SDK and he&apos;s been writing this book called crafting interpret predators and it&apos;s like one of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:10&lt;/strong&gt; · the best technical books I&apos;ve ever read like it&apos;s so well uh written like I remember I was sitting at the airport um uh I don&apos;t know where I was flying and just reading the book and hacking my my language but yeah basically so um regarding language I thought okay if you can Target the dark kernel file you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:33&lt;/strong&gt; · um build a language which uh works the same way cotland does to Java like they both Target the same underlying system so they can have a very deep compatibility and using this uh custom language um you could have restrictions in in in the code and because you write the compil yourself you can inject as much meta information you want into the build build step um and then actually&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:01&lt;/strong&gt; · save the widget as code and modify it because you&apos;re controlling all of it you&apos;re controlling the compiler you&apos;re controlling the right the transportation um so that&apos;s one thing I&apos;ve looked at it uh looked at for a bit&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:18&lt;/strong&gt; · um but I mean it was mostly a fun project and actually building a custom language to then support some other language to convert some visual way that like if that isn&apos;t m I don&apos;t know yeah that&apos;s I&apos;ve also been exploring instead of doing my own language I&apos;ve actually been exploring like a node based um logic Creator so like instead of you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:40&lt;/strong&gt; · know yeah writing code you&apos;re just like connecting nodes so you can say like when this happens do this or you know when I tap on this button I want to trigger this event and it&apos;d be kind of an interesting way because I feel like there&apos;s a lot of applications with flutter that you could build with just a node base like something that doesn&apos;t need a lot lot of crazy logic something just like when a network request comes in either stream or do a future do this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:04&lt;/strong&gt; · on a repeating on an interval you know just like you can do so much with just that um you could build a counter application easily with it um but CU then your state is just like amorphous and can be stored in locations you could choose like think of how easy it would be to once you have a node base Editor to say like when I&apos;m storing this value save it to SQL light save it to Firebase save it to here like and then just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:28&lt;/strong&gt; · I I liter I literally when back in the day when I was actively developing widget maker I had this phase where I was just thinking about exactly this like just imagine okay yeah you have an application where you want to get the user data if you have something in your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:43&lt;/strong&gt; · uh cached uh storage you want to get it from there show it then do you tap something with a note based workflow what you could do is you could have a note which is your network user you could connect that to some other uh block which which first tries to go to the cachee block and in the end all you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:02&lt;/strong&gt; · do is you plug that note into the actual widget like just imagine a visual canvas where you have your widgets laid out like you do with the storyboard and if you enable the code overlayer you see all the code connections on top of the widgets so you could actually connect the text which is on the page with a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:23&lt;/strong&gt; · note which supplies some sort of text and the cool thing is you could run this application in the editor and see all the data flowing from one point to another and just debug it because okay you have this bug what what is one thing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:38&lt;/strong&gt; · which helps you with debugging just gaing as much information about the current state of the system so what you do is you set break points you do print statements you go for your application think okay I should be in this so the user should be logged in is he logged in and in this case you could actually see okay I see all of the data which is currently being held State I see how the data is connected and mhm like that that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:03&lt;/strong&gt; · could be pretty cool yeah and the cool thing is you could do it in an imperative way with flutter 2 where you could have individual screens that are covered by nodes you could have the entire application that could be running on nodes because what&apos;s so powerful about that is then you could share those template so you could have that logic that you could share around because it just be a file at that point um yeah and I just I think about you know there&apos;s other language that have this and you know there&apos;s so many like I just I call&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:31&lt;/strong&gt; · them like clickable applications that you could just you know you click here it goes to this screen you click here it goes to this screen you just you go to this screen it gets a list from this URL and it&apos;s I don&apos;t know you can build some really cool static applications this way yes and I mean even more complex ones like even those complex applications have a big uh part of them are static&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:51&lt;/strong&gt; · like okay they may have some sort of fancy Al algorithm in there they may have some machine learning in there but but those are mostly modules like you can wrap that module in some sort of visual node and just just using a system and if you&apos;re this far and you have all those presets assets made like what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:11&lt;/strong&gt; · stops you from building an application a couple of days like okay you want to login totally just use the login module you want to have a user profile use user profile you may want to customize it sure go ahead yeah and yes like the abilities are there uh just has to be developed and I mean you never know where it&apos;s an actually good idea like this this could even be like it could be bad we don&apos;t know we haven&apos;t done it yet um but I think there&apos;s a lot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:44&lt;/strong&gt; · of movement going into the uh direction of uh the no Code and low code environment where the uh next layer of abstraction to like the very beginning was like people drilling holes and cards which magically told the computer what to do some point somebody thought okay let&apos;s not do holes let&apos;s just try to write texts like moving registers and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:12&lt;/strong&gt; · additions and at some point okay why are we doing this we could write some language we can actually read and write uh which is easy for a human for a computer program language is super weird what what what should a computer do with a class statement and but because the compiler is there which converts that into the lower language which then goes even lower and using this next layer of abstraction like going from visual to language to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:41&lt;/strong&gt; · instructions and to machine code you have so much more abstraction could be way more efficient if it&apos;s done correctly I think at some point we&apos;re not going to program programs using a language forever like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:57&lt;/strong&gt; · at some point it will change like even if it&apos;s AI which is just going to take over and build something on its own but something&apos;s going to change at some point yeah I mean and also what&apos;s crazy too about the node base editor is you can actually support plugins because a pluging can just Define what its inputs and outputs are in a simple file so that&apos;s pretty cool already but also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:20&lt;/strong&gt; · think about once you have a node base system you could actually then use machine learning to generate the most optimal solution of data flow you know like you could literally have all the inputs and yeah it&apos;s crazy like you could find logic bugs and I don&apos;t know there&apos;s just there&apos;s so many possibilities when you&apos;re in a node-based editor because all of a sudden now you have like basically these Legos that you can connect everything to each other of course you can run into some crazy problems like an in plus one problem but um yeah there&apos;s some pretty&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:46&lt;/strong&gt; · interesting things you could do I think the most important thing with a note based editor is and which a lot of uh those not based Solutions which are there like is the integration with actual code like how easy it is to drop down a layer because a lot of times you have to write boiler plate code which is really not fun to write you have to go through some sort of calization deserialization you have to go through maybe a type versus un type thing and if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:15&lt;/strong&gt; · you have this barrier where it is more work to actually write the code in in code than um to just write all of it in code like just imagine most of the notes work but you have to write two notes which you have to write in code and if you have to maintain those two notes as much as you would have maintained your whole code base uh then you lose the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:40&lt;/strong&gt; · power it&apos;s not worth it but if you have some way like one click connected and it&apos;s all whatever it&apos;s typed it&apos;s it&apos;s working and you you don&apos;t have the friction between going between those two words and you can just separate those um I think that would be an important thing right and for me what I&apos;m thinking of when I if if I&apos;m building this solution is maybe actually using the node-based editor to write all the code generation for you so like you could so there&apos;s two&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:09&lt;/strong&gt; · multiple approaches obviously you can take but basically you know as you connect stuff it&apos;s actually generating the dart code and then that way you have you know the all the aot performance kind of stuff and then the cool part is what I&apos;m thinking about is you could actually support custom scripts written in Dart but you could even do it one step further where you know obviously that would work out of the Hood um or out of the gate you could just connect it but what if you supported like these like I don&apos;t know if you&apos;ve ever used Swift playgrounds but like magic variables so then you could say like you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:37&lt;/strong&gt; · know you have like the name of the class and like as you&apos;re editing in the node editor you could actually have this like Dart environment which you can have these special variables that are passed in that then when you generate the code it generates the correct names and does everything for you I feel like all of this uh for it to really work it would have to be deeply integrated in into your environment like uh like one one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:01&lt;/strong&gt; · thing that&apos;s super annoying if you have these variables and you&apos;re trying to debug and in the end it turns out you misspell the the uh yeah the and the regx didn&apos;t catch it or something like that so uh if you have something like this and you pair it with a very nice&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:19&lt;/strong&gt; · integration into into your ID it just feels flawlessly connected then that&apos;s that&apos;s where a place where it really shines Ian I mean just think back where mod modern idees like what do they do for you I couldn&apos;t imagine myself programming nowadays without an ID which has autocomplete syntax checks go to definition like that&apos;s a big thing and if you lose this for these note based&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:48&lt;/strong&gt; · things because it just cannot comprehend and connect those then that&apos;s a big stepb back but if you have those like I think that that has the big potential right I mean even you know like when we have complete ml right now which can use machine learning to do autocomplete suggestions and vs code but think about taking that one step further where you can have a node and it has suggested nodes which is kind of nuts so like you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:11&lt;/strong&gt; · could say yeah exactly like oh you want to do persistence you want to you know syn it to Firebase you want to do whatever and you know at first glance you think well maybe that&apos;s going to generate a whole bunch of code but because of the abstraction you&apos;re actually able to optimize it so you can have like just like how Dart has two different modes you have the debugging and the profile and release phase you could actually have separate modes for you know all these different parts yes and as we talked before in debug uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:38&lt;/strong&gt; · debug mode you could have all sorts of different debug information there the current states the transitions data flowing you like there&apos;s a lot of possibilities yeah it&apos;s it&apos;s pretty insane So speaking of um that have you worked with um the dart analyzer at all I&apos;ve been taking a look at it for a bit um it&apos;s like it&apos;s super interesting to have a package which is implemented in Dart to analyze dark code and get&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:08&lt;/strong&gt; · information about itself um have you yeah I&apos;ve I&apos;ve worked a little bit I&apos;ve been starting to build my own like kind of Dart parser but yeah it&apos;s it&apos;s pretty insane um the analyzer is so powerful but the applications I&apos;m working on have to run on the web which the analyzer doesn&apos;t exist on the web which is kind of unfortunate but um so uh have you I I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:28&lt;/strong&gt; · think weren&apos;t you working on like a plugin for Dart or the dart analyzer um so basically I got pretty um Fed Up by the fact I had to localize the strings manually and just go in there extracted go into our files so I thought okay when I did an intell plugin before for fun let&apos;s try and see what I can build in an evening um so I built this little plugin which uh helps you localize Keys basically with a quick action which you INX with the old Enter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:01&lt;/strong&gt; · key um it copies the value into a Json file and replace the code with the correct call um and then I thought okay this was pretty tedious to write like Java with intelligent apis and all that stuff like like most of the stuff was undocumented I had to search through so many things I thought um it would be way better and cooler to actually write this code in Dart like write a d plugin for that wow so uh I stumbled across this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:32&lt;/strong&gt; · project or this thing called um analyzer plugins which basically is exactly this um it allows you to write a plugin which is written in Dart uh which runs inside the analyzer which um in modern ID for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:48&lt;/strong&gt; · example intellig and vs code um allows you to inject or add for example suggestions go to definitions uh refactorings and all that stuff and with this like during this podcast I just thought when we were talking about the program language with this with my custom program language I could even Implement a goto definition cross boundary like in D I could run my my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:15&lt;/strong&gt; · compiler and analyzer on my custom program language and allow the user to go from the dark code inside that custom language because all the code which is there is written D you don&apos;t have to do some weird plugins and super easy so so that&apos;s one thing um I&apos;m exploring right now like I&apos;ve been trying to set it up uh it&apos;s I should be able to to have a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:42&lt;/strong&gt; · few experiments running in a couple of days um but that&apos;s super interesting have you heard about this yeah yeah I um yeah just been seeing the kind of stuff that you&apos;ve been doing I mean the the analyzer plugin is just like incredibly powerful and like you said earlier like I couldn&apos;t imagine developing without an IDE now like there&apos;s so much work that the analyzer does for you as a developer and I feel like um the ways that we can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:07&lt;/strong&gt; · solve our own pain points to create solutions to things that we&apos;re facing every day only makes us better developers um yeah and I I love the fact that you did it with the internationalization plugin because that&apos;s for me a huge um thing already cuz like how many strings do you probably have in an application you probably have like hundreds maybe maybe even a thousand on a big application and you have to go into to each one of them store them in a Json file and yeah it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:33&lt;/strong&gt; · a it&apos;s a tedious process that&apos;s for sure yeah actually after I published the plugin um uh Dominic pointed out that uh something very similar exists for vs code and sure enough it also exists for intellig J uh which is the Intel plug-in by localy um which does pretty much exactly the same thing um only drawback is that it only work works with their format and way they do the code and um I had a like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:04&lt;/strong&gt; · it was working okay um my case it&apos;s more flexible like you can specify what the code is which is going to be replaced and stuff like this like for example if you&apos;re doing your own localization system where you&apos;re using a custom package or maybe sometimes you&apos;re just using a playo Json file which you load in with with um as a file and in this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:26&lt;/strong&gt; · case you can use this plugin to write to loc with your own system um but yeah like things like these like it went from okay I really don&apos;t want to do this task this is so annoying to Oh I&apos;ll just do a few strings here and there I mean it isn&apos;t any extra work and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:47&lt;/strong&gt; · I mean it is a small thing like you could sit down and do this but just the feeling of being productive and actually not sitting down to do the tedious work but to actually express yourself and work on actual problems which you&apos;re solving instead of just coding right&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:05&lt;/strong&gt; · exactly well one thing you could do though is because it&apos;s an analyzer plugin wouldn&apos;t you be able to write a plugin that could just automatically extract all strings to a Json map for you on existing code so so that plugin um I was doing it in pure intellig and that&apos;s where uh this idea um to make it in dark came from so I haven&apos;t actually looked at all the apis yet um but it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:29&lt;/strong&gt; · could definitely be possible like um it&apos;s could be hard to figure out which strings to actually localize like sometimes you have strings which are which should be localized and some shouldn&apos;t for example strings in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:44&lt;/strong&gt; · variables where you just save something you don&apos;t want to localize but then again you may have variables which you do want to localize which just holds the current user output or something like that this yeah um so I think just manually going in there and selecting those one by one is a fair compromise&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:03&lt;/strong&gt; · because it&apos;s little work when you see something you just tap two keys and it&apos;s done and on the other hand you don&apos;t have to rely on the system to magically figure out which strings to extract and which which leave in place well you can always you know use start decorators to decorate any variables you don&apos;t want to have extracted of course it&apos;s it&apos;s a valid approach um yeah it was just fascinated by the uh ability to write these plug-in extensions for an analyzer&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:33&lt;/strong&gt; · because that&apos;s the thing you&apos;re using daily like your um IDE which does so much stuff and you&apos;re if you&apos;re able to to customize it even further for packages or systems you are using that that&apos;s in my opinion is a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:51&lt;/strong&gt; · pretty big uh productivity boost yep and not only that but you just learning about how the analyzer Works under the hood only makes you use Dart better too because um you start to really understand how powerful everything is and how you can optimize things for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:08&lt;/strong&gt; · sure so next I want to talk about um storyboarding and I know that your widget maker already does some of this like what kind of approaches did you take when you know having to lay out multiple canvases you know and be able to pan around and um you know what what kind of inspired you to do that so you&apos;re talking about the widget maker correct yes so um well basically um most&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:33&lt;/strong&gt; · of the graphics application do this and the idea was from the beginning to be um somewhat similar to what sketch figma and all those applications are um and implementing it was pretty interesting I think I answered to a stick overflow question so I was using a stack with a transform and basic basically when you scroll you&apos;re just transforming and making them more and bigger and using&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:02&lt;/strong&gt; · the stack I was able to position all the widgets but there was one problem is when you transform the stack and make it smaller the area where it can um get user inputs um is also smaller so what happened is you when you tried I think when you try to to drag outside of the Middle where it was actually at it didn&apos;t work so what I ended up doing is actually copying the whole stack code and just removing one line in the test render or hit hit test where it was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:35&lt;/strong&gt; · checking if the position is actually in the bounds and I was okay don&apos;t check where it&apos;s in the bounds you are hit uh that was a pretty interesting thing um how did you do this in the in Your Story Board uh hearing you say that makes me realize all the incredible amount of effort that went into my solution that I probably should have thought about doing that um so for what I did for my storyboard is I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:00&lt;/strong&gt; · I faced the same problem as you like my first approach was I wanted to be able to support zooming in and out of the canvas and um all this and this was my my third iteration on trying to figure this out um I came up with a solution I don&apos;t know if it&apos;s the correct solution but I&apos;m using a stack just like you using the offset to uh pan around but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:19&lt;/strong&gt; · the only difference is I&apos;m transforming each child that&apos;s on the stack and aligning it so then um I have the widgets that you know can expand and contract and then I transform those to where it actually works pretty well and pretty performant but yeah cuz I was facing that issue where if you once the the stack started shrinking then yeah you couldn&apos;t tap on anything else I feel like this approach is just different but yeah um it was definitely a lot more work to try to figure out um everything and in fact before I did this last um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:50&lt;/strong&gt; · one that I just published I was actually transforming each child on the canvas So in theory I was wanting to be able to support dragging each screen around to wherever you want which I may still in the future but this approach is so much simpler I can you know put a grid view on the canvas I can put a stack or a uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:10&lt;/strong&gt; · a wrap or even columns and rows and then I transform those down to scale and have them you know in a column that all go together so kind of different but uh yeah it was definitely interesting to try to solve that cuz it&apos;s not exactly obvious there&apos;s no like you know flutter widget out there that are doing this it&apos;s you know a canvas approach is definitely something new kind of concept&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:31&lt;/strong&gt; · but like once you do it it&apos;s like wow that was really simple you know like you you expect it to be so much more complicated and it works so well with performance cuz it&apos;s clipping out the bounds and um only rendering what&apos;s on the screen yeah I think like um there are very few cases where you&apos;d actually&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:48&lt;/strong&gt; · want to do a custom render object but I think if you would be having this inside the framework which would have to support a huge variety of use cases making this a render object would probably make sense because then you could also Implement stuff like lazy loading like a canvas. builder where you may have millions of widgets but only actually build those which are in the viewport lazily destroy maintain State&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:13&lt;/strong&gt; · destroy State and do all those things yeah um but yeah I mean if you don&apos;t need those wi bar and go this road because that that&apos;s something which gets complicated then you have to think about the life cycle of children how they laid out how to efficiently do all that stuff and I mean it&apos;s always best to stay on the topmost layer the widgets layer and try to use what&apos;s on there and only drop down if you really have to um but I mean&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:41&lt;/strong&gt; · if it works it&apos;s a good solution right yep I&apos;ve tested it with about maybe 150 200 screens on the canvas at the same time and it seems to work pretty well um yeah you taking this new approach helped a lot because before it was having to render everything off screen too and overflowing which that was causing huge performance reason so or performance cost so now because I&apos;m clipping the stack on the viewport and doing that and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:10&lt;/strong&gt; · then clipping each child too it really reduced the amount of overhead I was facing I see you yeah but it it&apos;s pretty cool though I built it because I wanted to um be able to create my flutter application with all my screens at the same time and just like you know change the theme pile and watch everything update or you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:31&lt;/strong&gt; · add a new screen or widget customize because the thing is it supports hot reload and it keeps the state of all the screens you can I even recently added a restart button so you could even restart the entire applications pretty easily which is crazy but I just found out how simple it is to do that um I was looking at device preview on how it works if you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:50&lt;/strong&gt; · just give your widget a new key and call set state it will restart the state of the widget I don&apos;t know I thought it was going to be so much more complicated but all you have to do is just give a new key to your material app and then it does basically it restarts so that&apos;s kind of interesting um I could have used that for other applications too like maybe like if the user logs out and you want to guarantee that it&apos;s a fresh app you could even do the same kind of concept where you could just give a new key every time a user logs out yes kind interesting so um one thing I wonder and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:20&lt;/strong&gt; · ask myself um I don&apos;t know where you&apos;re dealing with this right now but what what would be your uh opinions on this for example if you have an application which relies on scopes for example you have a few screens which only work if the user&apos;s logged in because there&apos;s no nothing to show on the user profile if there&apos;s no user how can you ensure that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:42&lt;/strong&gt; · all these screens are in a somewhat valid configuration for example if you have two screens which are mutually exclusive for example a user logged in screen which expects a user to be logged in and a user logged out screen which expect the user to be locked out how can you visualize both of the screens at the same time maybe have in the worst case&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:03&lt;/strong&gt; · wrong data which is marked as that um but still not crash yeah for me what I did is I introduced this concept of Uh custom screens which allow you to add any number set of screens with whatever kind of Constructors you want so outside of your routes and everything else you can pass in the data explicitly so you can say like you know you can say this is is this configuration for this widget this is this configuration you could even have the same widget with different sizes even um and the purpose of this is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:33&lt;/strong&gt; · exactly like what you said um sometimes you want to pass in dummy data like for example um if you wanted to preview a detail screen you like with the storyboard you don&apos;t have to wait for the um Json request to load because yes in the storyboard you can still tap and go to the details you can also view the details with dummy data and just modify all the you know hot reload all the changes without having to run your application which I think is pretty cool because there&apos;s many times that you&apos;re doing a detail screen and maybe you&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:04&lt;/strong&gt; · trying to find like that perfect Json item that has all the data or like it uh you know there&apos;s so many edge cases and then so with this approach you can actually edit not only the list view the details and the sub details all at the same time so you&apos;re taking the approach of having these screens uh very modular without any exent dependencies like no stream ERS no block Builders none of those but passing the data instead yep so so the thing is how could&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:34&lt;/strong&gt; · you go ahead and make this with the least B paid code possible so what I could think about is for example have the screen take all the data and have a screen which wraps the screen which then actually provides data with a stream B but then again you have two widgets which essentially could be one widget how would you be dealing with this you could mock it for sure you could even do like uh you could wrap the widget with a a presenter widget or something and then all it does is just mock the the logged in state of the the scope of what it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:03&lt;/strong&gt; · looking for since it&apos; be looking up for the block with the logged in state you could literally provide that so I see that&apos;s interesting so you could have a package which just contains a bunch of marks which rep all of these that&apos;s interesting yeah that makes sense you wouldn&apos;t have to modify your actual app and you would still have the benefits of being able to and I mean you could show uh mock data in your preview like you want want to see what user it is you don&apos;t care who&apos;s locked in but you want to see the data and for me too like I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:34&lt;/strong&gt; · on at least where it is now like 0.1.1 I&apos;m trying to support um you know pure widgets at first like you know those those work best second thing is just regular widgets that then you can you know log in or do whatever but the reason why is you know there&apos;s many times that like you may write a widget&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:54&lt;/strong&gt; · that has way too much much logic and I&apos;m trying to encourage you know basically creating like a view model so like you have just a widget that&apos;s just presenting the data because if you do that then it&apos;s super easy to customize it and add a bunch of stuff and test everything because then you can use the view model to then present that data based on the data that comes in um yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:16&lt;/strong&gt; · it&apos;s just yeah it&apos;s really depending on how you write flutter code too because there&apos;s like a ton of different ways how you can create applications and how you create screens but yeah I&apos;m trying to create the abstraction that&apos;s above the block layer so literally just showing the widgets themselves going back to to the mocking and stuff like um if you then go the the additional step to work on the smoking I can really see it the block Library work um pretty well with this because it&apos;s very um like view&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:46&lt;/strong&gt; · modly you have the states which are very encapsulated so you could actually have um some sort of I don&apos;t know annotation or even just a helper method which where you all you do is you provide the state object which you want this screen to to access and internally you could hook it up with your um block providers and do all of that stuff and just load in this one state because if you are following the block package way and you have all your blocks in there you could have a fully functional app mocked with like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:18&lt;/strong&gt; · literally a few lines of code and you could have all your apps visible so so that&apos;s also one thing if you&apos;re talking about just pure widgets a lot of times in my codebase I&apos;m thinking okay have I done this widget before is it somewhere in my code base uh where I don&apos;t know or I have do I have to redo it uh so what I usually do is just open the app navigate around and try to see where I have a similar widget but using this you could just have a pallet of widgets like um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:48&lt;/strong&gt; · yep widgets which you&apos;ve been using everywhere and just see yourself okay where is this widget and you could even do something like navigate to the widget in your ID like tap the widget and show in code and then okay I know this widget exist and just using it this way that would be super powerful what I typically do with blocks though is for me I like to have most of my logic in the blocks like I almost use them as like a repository for triggering actions and my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:18&lt;/strong&gt; · widgets are literally um well I guess because I&apos;m doing a lot of web applications now all of them have to be able to support like deep linking so if they&apos;re not logged in like it&apos;s the widget can log itself in like you can actually log in from that screen I don&apos;t necessarily have to go to the login screen to do it so like I just show a popup or a dialogue so that yeah that&apos;s the only different approach that I guess in my applications I haven&apos;t faced that too much because my blocks are kind of separate like my widgets can live outside of my blocks I see yeah for me most of my um scoping is very very um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:50&lt;/strong&gt; · implicit in the widget she like I have widgets where I expect this widget can only ever be seen if the user logged in so I&apos;m actually just casting to a logged in user State just being sure okay you you have no way of navigating into here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:07&lt;/strong&gt; · without being locked in because if you would be locked out the widget tree above would be rebuilding and destroying this widget immediately so you would never have the chance to be in here without being logged in so for me um I have a very scoped architecture I actually even use uh mostly one nested Navigator where I include for example a use authenticated scope like um this way even though I&apos;m in the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:32&lt;/strong&gt; · sub tree I can push to that route without re like pushing on top of that scope like I&apos;m always pushing under it so anything which is inside this app has access to that block but there&apos;s also widgets which are outside of that scope which don&apos;t have access to that so for me it&apos;s very implicit so it would be very very um helpful to have this way of mocking that because most of the widget depend on some kind of block to get data from gotcha that&apos;s very powerful yeah um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:01&lt;/strong&gt; · and I I&apos;ll definitely be looking into that more maybe we can talk about that more next episode because yeah that&apos;s something I&apos;m definitely wanting to support is be able to support all different kinds of State Management Solutions you know like Redux like be able to pass in your store and to see you know a mock state of everything um yeah that&apos;s super powerful so uh what do you have uh going on this week so this week what have I um I will be doing a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:24&lt;/strong&gt; · bit of work on widget maker um mostly just doing a bit of cleanup um documenting stuff so if anybody&apos;s interested in contributing talking uh just just talking about the ideas and potential Solutions um be sure to to hit me up uh open an issue send me an email what uh whatever you want um also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:46&lt;/strong&gt; · probably be exploring the analyzer plugin and see where I can um build something useful like we just talked uh quite a bit about blog and we talked about this problem before that when you dispatch an action and you want to see what the actual code is which is executed um you control tab on the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:05&lt;/strong&gt; · action and where does it lead you to the Constructor of the action there&apos;s not much information uh one from there because there&apos;s there isn&apos;t actually any logic so I thought it would be pretty interesting to write an analyzer plug which actually takes you to the code where the action is processed um so that&apos;s one thing I&apos;ll be taking a look to um how about you uh yeah going to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:27&lt;/strong&gt; · have a pretty busy week I&apos;ll be talking at flutter Europe this week so make sure to um look out for that we&apos;ll have links to everything we talked about in this episode as well in the description um I&apos;m going to be working on the flutter editor and kind of more going down the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:42&lt;/strong&gt; · rabbit hole of uh for me I&apos;ve just discovered Proto Buffs and grpc and the amazing world of everything that is binary and http2 so I&apos;m going to be you know going hardcore into that uh Rabbit Hole so we&apos;ll see what comes out of that um and and also just you know continue to make updates to the storyboard as well as the golden layout package that I made so uh thanks so much for listening today if you have any questions like always you can reach out to us on Twitter um GitHub or just um you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:11&lt;/strong&gt; · find us uh our emails are out there um if you want to uh support this podcast please uh you know give it a rating on iTunes that&apos;s always really helpful and uh make sure you you know comment on our repositories on GitHub you know we like to see the feedback we want to see what you guys are thinking um feel free to make issues or PRS or whatever thank you so much for listening we&apos;ll see you um in the next episode thanks&lt;/p&gt;
</content:encoded></item><item><title>Flutter Adaptive UI and Web Assembly</title><link>https://rodydavis.com/podcast/creative-engineering/flutter-adaptive-ui-and-web-assembly/</link><guid isPermaLink="true">https://rodydavis.com/podcast/creative-engineering/flutter-adaptive-ui-and-web-assembly/</guid><description>This podcast episode discusses Flutter Adaptive UI and Web Assembly while also updating listeners on the core features of GitHub becoming free for teams.</description><pubDate>Tue, 31 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Flutter Adaptive UI and Web Assembly&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;MsqAGkrKVnc&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;BigScreen: https://www.oculus.com/experiences/quest/2497738113633933/
Github free for teams: https://github.blog/2020-04-14-github-is-now-free-for-teams/
Codepen for Flutter: https://codepen.io/flutter
Flutter internals talk: https://www.youtube.com/watch?v=PnWxW21vDak
The Rust book: https://doc.rust-lang.org/book/&lt;/p&gt;
&lt;p&gt;Norbert Kozsir - @norbertkozsir&lt;/p&gt;
&lt;p&gt;https://twitter.com/norbertkozsir&lt;/p&gt;
&lt;p&gt;https://github.com/norbert515&lt;/p&gt;
&lt;p&gt;Rody Davis - @rodydavis&lt;/p&gt;
&lt;p&gt;https://twitter.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://github.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://youtube.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://rodydavis.com&lt;/p&gt;
&lt;p&gt;Our podcast player:&lt;/p&gt;
&lt;p&gt;https://rodydavis.github.io/creative_engineering/&lt;/p&gt;
&lt;p&gt;Follow on Twitter:&lt;/p&gt;
&lt;p&gt;https://twitter.com/CreativeEngShow&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · welcome to creative engineering this is our second podcast with me I have my co-host Norbert how have you been during the quarantine great actually it&apos;s been a uh pretty productive week um have been doing good progress on all my projects how&apos;s it been for you doing pretty good you know just putting out that quarantine content making videos and it&apos;s uh pretty crazy I&apos;m excited to go outside again whenever that will be so sometime yeah all right so we have some&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:27&lt;/strong&gt; · uh followup items um first of all uh did you know that a GitHub is free for teams yes um I heard about it what exactly does it mean well as far as I can tell the all of the core GitHub features are now free for everyone which is super cool because um I know that at least for me I have some private repos that you you have in the past only been limited I believe like maybe three people or two people as collaborators so now no matter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:55&lt;/strong&gt; · what you can you know have as many people join which is super cool um as as well as you know creating organizations and have as many unlimited uh private repositories question is uh if everybody starts on this will it be free afterwards and I&apos;m guessing not but we&apos;ll see oh yeah it&apos;s super interesting like what happened since Microsoft quiet GitHub everybody thought it&apos;s going to go down and everybody was ready to switch to GitHub and now GitHub is like the thing yeah it&apos;s uh but it&apos;s also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26&lt;/strong&gt; · been really good too I I mean I was paying for GitHub Pro subscription too and even cancelled it when they made it free but then I went back to paying for it because you know not only do I get to support things but I realize that if you&apos;re on the Pro Plan you get like the beta features which is super cool so that&apos;s what I&apos;ve been using it for so we have another thing on our followup um some just quick VR updates uh I watched last night the Top Gun movie in 3D in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:55&lt;/strong&gt; · big screen and I got to say that was one of the coolest things that I&apos;ve watched a while I don&apos;t know how they did it but they they were able to take like an 80s movie and remaster it for 3D and it was super cool and what was neat about the big screen uh Cinema experience is the movie was like $5 here in the US and it was in a retro theater it was so cool so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:19&lt;/strong&gt; · you were sitting in a VR Fredy Cinema watching an actual Fredy movie yeah yeah it was it was pretty incredible and then uh yesterday they also it&apos;s going on today as well but um they are doing some live a live music festival which is super cool hosted by live X live and that was really neat um of course you know it just depends on like taste and music and they didn&apos;t really pick a theme it&apos;s like they went from a rapper to a really R&amp;amp;B and kind of like jazz&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:50&lt;/strong&gt; · band to then a DJ like all just back to back to back and so there&apos;s like it&apos;s just all these different artists but it&apos;s super cool new artists that I haven&apos;t even heard before but but now follow them on Spotify which is cool it&apos;s nice how many people were there yeah there was uh when I first joined there was over50 people just in the one room I was in in one place like in one virtual wow that&apos;s crazy yeah and it was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:15&lt;/strong&gt; · pretty cool it was funny one of the hosts uh you could you they were both in quarantine but one of the hosts on her webcam you could tell she had bad internet cuz like the guy on the right is like crystal clear and then she is like all broken up it&apos;s so funny it was awesome yeah and then last I just wanted to talk about the new fit bait song that came out and um have you seen any videos about it yet yes like I&apos;ve actually seen your video about it yeah it is it is incredible I I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:44&lt;/strong&gt; · still sore from playing it it is I I hope they make a whole music pack on it because this is the kind of stuff that I use VR for is to work out and to to really get your heart rate up and man it is it&apos;ll wipe you out oh yeah I&apos;m super jealous I um not yet able to try it out because I don&apos;t have my V VR headset with me right now yes but yeah definitely first thing when I have access to it uh I&apos;ll try that so Cod pin supports flutter that&apos;s that was a surprise um super interesting&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:18&lt;/strong&gt; · um like as far as I um understood we already have Dart Pad but codepad adds this social layer onto all of that being able to Fork our people&apos;s uh Flor creations and just sharing all that stuff mhm have you used codepen before I feel like I&apos;ve seen it in blog post but personally I haven&apos;t really used it um I&apos;ve used it for like experimenting with it for a bit one thing which uh code pen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:47&lt;/strong&gt; · or Dart pad um could actually be pretty useful in the near future is when you&apos;re doing a workshop or something like that and people don&apos;t have Flor installed or don&apos;t have all that stuff there um just being able to open up a website and doing all of this would be great but the problem right now is it doesn&apos;t support packages as far as I know um it only has the built in ones um which is Prett limiting if you want to use something like provider or or things you might be&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:13&lt;/strong&gt; · using well you mean you can always just copy the entirety of provider and put it in the single file that sounds crazy I like it yeah speaking of which I um so in my flutter editor last week I have built a kind of a lexer like a parser for Dart files um and it has triggered&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:36&lt;/strong&gt; · this crazy idea of well what if I take an existing flutter project and have if you just have pure widgets meaning you don&apos;t have any packages um what if you just have a one-click export to dartpad where it takes all of your classes and exports them to a single file and then then you just have and it you know organizes all your Imports at the top and then you have your main file so it&apos;s something I&apos;ve been experimenting with and so far um making good progress so we&apos;ll see um so we can also talk about&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:03&lt;/strong&gt; · why code pin doesn&apos;t support packages and that is because the dart Services API is what dartpad and cod pin are built on top of are you familiar with that um I haven&apos;t can you uh tell a bit more about it yeah so um even my flutter editor uses this uh Dart Services API what it is is a rest API to um your code&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:27&lt;/strong&gt; · so when you don&apos;t have the flutter or Dart s K installed you can still use a rest API to pass in your code and then you get like a list of you know fix options or an um analyzer options you can even do um pass in an office offset&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:42&lt;/strong&gt; · and be like okay what are the autoc completion results for this one area or what are the fixes that I can apply and you can format your code and one thing I love it for is you can use the DDC compiler so you can actually compile for flutter web or you can compile the release version but that one takes a lot longer CU it uses the dart to JS um and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:03&lt;/strong&gt; · I even created like a simple example that you can now in my flutter editor one click button it compiles your code and opens up a new window with your actual compile code that you can edit and do whatever with so I just I&apos;ve been super fascinated with this API and the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:20&lt;/strong&gt; · just the flexibility that it gives you for you know introducing these kind of dynamic widgets and having if you wanted to not saying you should but you could have a web view that you pass in a serers side rendered Dart file compile that and then show the rendered file in your app interesting interesting yeah okay&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:42&lt;/strong&gt; · wow yeah talking about uh Door pad for and web and all those things um do you have some sort of best practices for building apps uis for all those different platforms because just building a mobile app is one thing but having code which works on all platforms without having a big mess is isn&apos;t that easy yeah uh for sure um so I wanted to talk about&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:07&lt;/strong&gt; · you know this thing that I&apos;m calling adaptive UI um what that means really is that we build these applications like you said that have to run on different platforms where you know in one mode they&apos;re running on mobile another mode they&apos;re running on tablet and another desktop and if we&apos;re not careful we&apos;re building this UI in such a way where you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:28&lt;/strong&gt; · know it&apos;ll you know it&apos;ll look fine on mobile and then we go to desktop and it just looks like an iPad version or if you go to the desktop and you come down to a website then it&apos;s like oh now it just looks like a desktop app it doesn&apos;t look like a web app um so we have to be&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:43&lt;/strong&gt; · you know very cognizant of when we&apos;re building these applications to make sure that it&apos;s not just the screen size we&apos;re targeting you know it&apos;s not just okay if we&apos;re past 720 we&apos;re on a tablet if we&apos;re on 480 we&apos;re on a mobile because breakpoints are not everything um what what have you done in the past to make sure that you your apps are responsive you know on different layouts like have you built an app yet that goes to mobile and desktop yes definitely like most of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:12&lt;/strong&gt; · um my apps nowadays um are built in some sort of responsive way because I use desktop heavily and mobile um so different things like first off sometimes you just have two very different layouts like for desktop you might have some sort of sidebar or something on mobile you might have that on the on the view um there I just have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:33&lt;/strong&gt; · two build methods or two different functions widgets which just have uh different um layouts but still use the same components like all they do is instead of using a column maybe they might be using a row also for me um just using constraint box a lot like setting the max width and stuff like that so one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:52&lt;/strong&gt; · thing that&apos;s uh looks very um weird in my opinion is when the button stretches um across all the surface like on mobile it&apos;s fine if the button is uh um the full width because it&apos;s not that big but on desktop or web it looks rather weird if you have a button which is bigger than whatever yeah exactly or or for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:13&lt;/strong&gt; · example like when you show a snack bar and you&apos;re on desktop and it shows the entire bottom width of the the bottom yeah it&apos;s um you know but it&apos;s one of those things that you know it&apos;s amazing how many components that footer gives you but if we&apos;re not careful you know just because using the stock components doesn&apos;t mean you get an Adaptive UI out of the box so you definitely have to be aware of how to build it what are you usually using or what are you usually doing to uh solve this problem so for me&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:41&lt;/strong&gt; · I I have a builder pattern uh similar to you but I use a layout builder at my top uh of my build method wherever it&apos;s like you know the the parent that then defines the child size so I I looked in the constraint box but that that&apos;s more like on the individual screen and widget level this is more like kind of on the layout level yes what I do usually is so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:03&lt;/strong&gt; · there&apos;s the two approaches you can take you can do the naive approach of just like returning the tablet version and then the mobile version which is pretty bad in my opinion because then you get like a lot of duplicate UI code but the thing that I do is I create an Adaptive list for example like a master detailed um if you&apos;re coming from IOS is like the master detail controller and then what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:24&lt;/strong&gt; · you&apos;re doing is you&apos;re just passing that selection up the tree to when you click on an item in the list you&apos;re just not doing an action immediately you then send it up to where the widget&apos;s being used and then you take an action so on a tablet it&apos;ll just update a selection and on mobile it&apos;ll navigate to the new screen what&apos;s cool about this is you don&apos;t have to have two separate list views you don&apos;t have to have your tablet list View and your desktop list View and your mobile list view interes but then it also um gives you the flexibility that you can use this widget wherever you want so if I wanted to embed it in a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:54&lt;/strong&gt; · search screen I could then use the same screen as a selection context so I could then push to the screen and then based on a selection I could pop back and you know do whatever I want with it yes and that also makes a sharing code much easier like I could uh imagine a package which implen a couple of uh adaptive UI components for example scaffold which has a sitebar or something else mobile I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:19&lt;/strong&gt; · think you&apos;ve been doing something like that on your you YouTube channel haven&apos;t you yep sure have yeah I built a adaptive scaffold for flutter that uses the new navigation room um as well as when you&apos;re on a mobile device it&apos;ll use the bottom tabs so it&apos;s it&apos;s really nice to be able like layout with navigation is one of the first thing people start to duplicate because&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:40&lt;/strong&gt; · when you have these different modes you have bottom navigation bar you have navigation rail you have a drawer you have top tabs you you have so many different ways to do navigation on all these different sizes and it&apos;s like if you&apos;re not careful you&apos;re going to just like duplicate all this stuff and you know if you keep it in a one context and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:59&lt;/strong&gt; · you share it you know you don&apos;t have these weird States like so think about in the previous example if you return a tablet version and a mobile version if you do something on the tablet version and then resize down to mobile you&apos;re actually in a new state you don&apos;t have the same state as the tablet screen so then you go back to the tablet size you&apos;re back on your tablet State you go to your mobile size you have your mobile State you could be 300 items down on the mobile version and then 500 items down on the tablet version and they&apos;re two separate lists interesting you also feel&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:27&lt;/strong&gt; · like your approach is very Flur like because it uh encourages encapsulation you don&apos;t want to do all these different components all you want to do you actually want to have this one component which just takes care of the thing it&apos;s supposed to take care of yep exactly um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:44&lt;/strong&gt; · so speaking of that I also want to talk about platform UI because um when building for devices um I know I&apos;m building on Mac OS but you&apos;re building on Windows and so what kind of things do you do to make sure that the user feels at home on a Windows device uh whereas I would do something that fills at home for Apple devices first off I try to separate or not make the app look like a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:08&lt;/strong&gt; · upscale mobile app like um app bars are great um but try to avoid them um in Windows versions because um most most apps don&apos;t have the app bar with the back button and stuff um so that&apos;s one thing I usually do and just just the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:26&lt;/strong&gt; · little things like not having comp components which um benefit uh which people benefit from on the mobile version because they have limited space but instead use something which actually uses the space that is available on desktop um but yeah I still feel like there&apos;s a lot to lot lot of work to be done um on all those components to make a web version or a desktop version truly&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:53&lt;/strong&gt; · feel like a desktop application do you use any fluent design that&apos;s the windows design language do you that actually I I don&apos;t like I don&apos;t like that design language too much and there there aren&apos;t too many apps on Windows who actually use that design apart from Windows itself and even Windows is not using that every application um so basically&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:17&lt;/strong&gt; · just just um building like I feel like the trend um from adapting to each platform to having a unique brand which works on all platforms is is becoming stronger and stronger I feel like nowadays you don&apos;t actually want to adapt too much to each platform and have your app look completely different on each device but instead have your own language and adapt to the small nuances you have on the devices like the overscroll on iOS the um top or status&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:49&lt;/strong&gt; · bar mecho is how it&apos;s how it&apos;s called and just doing those is I feel like a better way to approach this yeah I agree the nuances is what make the difference um especially when you think about you know on iOS you want to build switches&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:05&lt;/strong&gt; · and Sliders and loading indicators that are this Cupertino style but then you don&apos;t necessarily want you know list tiles or scaffold to be you don&apos;t have to have a Cupertino scaffold uh by doing it in this way it allows you to make it feel at home but not obtrusive and you still get to stick to your brand also one thing that&apos;s another easy win when you&apos;re building this in an adep of way is well what about when you show an alert this is like a perfect example of um using an iOS alert style because a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:36&lt;/strong&gt; · user expects a certain alert to look some a certain way or when they&apos;re picking a date you want to show the Cupertino date picker or when they are picking a time you want to show like the timer in the bottom sheet but then on Mac OS you do Mac OS way of doing it and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:53&lt;/strong&gt; · and almost always I think the first thing that you can do is don&apos;t develop a touch in mind on desktop because there&apos;s so many things that you do on desktop that you can&apos;t really do on mobile and vice versa so you know you don&apos;t want to show a bottom sheet that you can drag things around in on the bottom you don&apos;t want to have things that you have to move around and slide on desktop but on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:17&lt;/strong&gt; · mobile you do and that&apos;s just all about building widgets that kind of adapt depending on which version they&apos;re on and show different input methods based on that um next I want to talk about you know well how do you approach dark mode because this is something that recently has become really popular in an official way because mainly because apple and so many other platforms now give you a dark mode option on your computer or phone now where it&apos;ll change everything um are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:44&lt;/strong&gt; · you talking about actually adapting it for the platform or just the theming itself the theming um well first off just setting the brightness to dark um goes a big way um like most of the time the app actually looks pretty good and most components um at least try to um adhere to the dark&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:02&lt;/strong&gt; · versus bright mode specs but yeah I&apos;m not sure like most of the time just setting it to dark goes a long way what what are you doing well I remember the first time that I had an app that I&apos;ve been working on for a while and then wanted to support dark mode and then half my app was invisible because all my text was black so I had to had to go&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:21&lt;/strong&gt; · through and make my text adaptive to where you know it can adjust its color based on the theme but beyond that I have been you know there&apos;s C like subtleties that you have to do you know where a color may be a slightly different color on the dark mode versus the light mode um you know you want to have like darker colors that aren&apos;t as bright you don&apos;t want to just blind the user when you go to dark mode as well as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:45&lt;/strong&gt; · you know there&apos;s there&apos;s just other nuances but like you know elevation is different shadows appear differently but flutter for example does a really good job of providing that all for you and even now on the latest we have a theme mode option inside a material app so you can actually switch between light dark and system setting which is super cool where as before you used to have this weird logic to do it interesting I didn&apos;t know about that like one thing um which I had um been problems with just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:15&lt;/strong&gt; · today was um buttons like do you know about a way to change the text color of a button button according to the color which it is on like I I more often than not I&apos;m actually Fighting the theme data and trying to figure out in which slot to put it stuff and embarrassingly often I just end up hard coding or not hard coding but putting the color into the actual text because I just can&apos;t find out how to configure my app Theme&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:46&lt;/strong&gt; · accordingly um do you also have some issues or problems with the theming in the top level component well speaking of a solution for your problem there is a really cool helper method that the theme data uses under the hood which is called estimate color for brightness and you can pass in this option and it&apos;ll give you whether it&apos;s or is maybe estimate brightness for color and so then you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:11&lt;/strong&gt; · pass in a color and it&apos;ll check to see if it needs to be a ligh or dark brightness to show the text I think this is what they use under the hood in a lot of different places but interesting yeah that that for me has been pretty helpful um so some other things that I run into when designing for theming is the first thing that I did was you know shaping&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:33&lt;/strong&gt; · sometimes like things look fine like a card for example looks great on light theme and then Dark theme it just looks kind of awkward like like the shadow is a little bit better like with the latest stuff they&apos;ve added it&apos;s so much better whereas before like it it was weird having the Dark Shadow and then it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:50&lt;/strong&gt; · almost it just didn&apos;t look out of Play It just looked out really out of place so what I usually did was on the Dark theme I would create a card theme that would just have a border outline that was like a light gray and that usually looked a lot better for me personally interesting approach yeah yep um so another thing too is our devices and our&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:11&lt;/strong&gt; · screens are and specifically applications are getting used in places that we don&apos;t even realize speaking of which is you know we have passive modes now it&apos;s crazy that our application can exist in a voice only context where we&apos;re not even touching our device and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:28&lt;/strong&gt; · um building experiences for that as well as well you you may think well I&apos;m building a mobile application I never have to worry about that but what about when someone is disabled and they can&apos;t see your application you still have a mobile app and you still need to support accessibility um to allow them to be able to communicate with your app in a semantic tree style to say you know with a screen reader so what what are your thoughts on that so I haven&apos;t been doing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:53&lt;/strong&gt; · digging too deep into the semantics and all that stuff but as far as I know for is actually doing a pretty good job out of the box um providing um most of the semantics tree like annotating text and doing all the layout things the lists um so as far as I know Flor out of the box does a pretty good job at um providing all of this and it should be pretty easy to enhance that like there are a few properties for example on the image&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:21&lt;/strong&gt; · think which uh I&apos;m not sure what it&apos;s called but where you describe the content and if you just pass the string describing the image the screen reader can automatically um read out the the image and say what is on there um what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:37&lt;/strong&gt; · have you been doing with accessibility yeah for me I I have a Tesla app that I built and one of the ways that I wanted to give an input for users that have a hard time seeing it is I built a chat bot so working with dialog flow I was able to take all the actions that I was doing on my car and put them in a conversation setting so then you could say you know set my car AC to 30° or you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:02&lt;/strong&gt; · know turn unlock the doors you know and then also turn on sensor mode and unlock the frun or whatever and what&apos;s cool is it will take each of those commands and Trigger them in a way and feedback and you know talk back to the user uh which I think is really cool because even if you can&apos;t like press the buttons you can still just open up a the voice button&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:25&lt;/strong&gt; · and just be able to communicate with the car so um other than that yeah I think flutter does a really good job with the semantic tree and um just taking advantage of that like so a simple thing that you can do to optimize for that is if your widget is like really weird then wrap it with like a tool tip or some kind of um message label for that as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:46&lt;/strong&gt; · well as whenever you&apos;re using a button or anything that takes the option tool tip or um I can&apos;t remember what the other option is but it allows you to you know Define what that is this would be like the equivalent like on the web when you have an ALT text for someone who can&apos;t see an image um which is really cool because you know your button instead of just being like oh this is my info button because they were using the info icon you can say exactly what that button&apos;s doing under the hood and as a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:15&lt;/strong&gt; · bonus when you&apos;re on desktop if you hover over that button it&apos;ll tell you what that button does um on the hover effect yes I&apos;ve seen that before yeah um and then finally you know there&apos;s also just uh so many wearables that we have now I know flutter doesn&apos;t uh support Apple watch or anything yet but I think it does support Android Wear am I correct on that I think my brother actually tried to compile for um theat&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:41&lt;/strong&gt; · which was supposed to be possible but I&apos;m not sure whether he was successfully able to do so but in theory it should be possible but I haven&apos;t actually seen a FL app on the watch myself um but I&apos;m still pretty sure it&apos;s possible so we&apos;ll have have to have to look at this in some future episodes definitely at least when it comes to Apple watch I&apos;m super excited because it&apos;s just it&apos;s one of those things that you know you think you&apos;re have responsive design like try&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:10&lt;/strong&gt; · making it go down to a 2in size or smaller and it&apos;s like there&apos;s so many things you have to do differently and um I&apos;m excited for when that becomes you know a whole lot more popular so we&apos;ve been talking about you know this this adaptive way to build your UI but how does flutter work like under the hood so Flor um does a very good job at um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:33&lt;/strong&gt; · abstracting all this away like you could have a widget which we talked about before about this adaptive thing which just works on all on watches on desktops or mobiles um but internally FL is actually doing a lot of work to abstract&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:50&lt;/strong&gt; · um things like State Management not having to keep mutable references to objects not having to deal with stuff like you have to do on um Android where you have to actually mutate views um in order to change them just providing this uh reactive interface for a programmer to program um objects in I actually did&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:12&lt;/strong&gt; · a talk couple of months ago on Flor Europe about uh render objects talk was uh called Flor War widgets elements and render objects um basically um showcasing exploring how the internal um framework works um what what render objects are what elements are and how all of those work&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:34&lt;/strong&gt; · together yeah so I would definitely love to hear more about that maybe we can have a full episode on that but um yeah so have you have you found that you needed to go down to the render object layer to build stuff or have you been mostly able to stay at the widget level&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:49&lt;/strong&gt; · cuz I know sometimes I have to go down to custom painter and do something on the canvas but um how often do you actually have to go down and do it if someone&apos;s starting with flutter do they even need to know how it works well most of the times you don&apos;t actually have to go down like for a beginner to intermediate or like even an expert&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:08&lt;/strong&gt; · wouldn&apos;t have to need uh the knowledge for render objects most of the times but sometimes I um still find myself going down there for example when um looking at weird behaviors like I remember I had this um thing um where I had a stack um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:24&lt;/strong&gt; · and I painted things outside of the stack B sound um which was because I was doing some sort of weird scrolling and zooming um but the thing was those widgets which were drawn outside of the stack balance didn&apos;t receive pointer events and that was because in the render stack it had a check if the pointer was outside its own bounds um without that check it worked perfectly so because I knew render objects I was able to go in there basically what what I did is I copied over all of the stack&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:56&lt;/strong&gt; · class and call it my stack and removed that if check um and I had a stack which received pointer outside and yes sometimes you you just uh have to go down there and and for for very custom or very weird behaviors it&apos;s super interesting but also just understanding that there&apos;s more than just widgets um that there&apos;s render objects that you can do contacts. off find render objects and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:22&lt;/strong&gt; · most of the time cast it to a render box and using that render box you can access the size and position which is super helpful if you for example want to do um um in a call back show some sort of information um which is sitting on top&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:38&lt;/strong&gt; · of a widget using the context and the render object you can actually locate that widget on the screen and display information accordingly there&apos;s so much I mean we could talk about this for multiple episodes going into a different direction how is this podcast actually hosted so that&apos;s a funny story uh when we first started started with this we were thinking you know we wanted to do this podcast give it out to the community but we wanted to see if we could build it in a way that um first of all it&apos;s cheap because we&apos;re first starting out but also uh in a way that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:10&lt;/strong&gt; · is really engaging so right after we released the first episode I created a podcast player in flutter to host our own podcast which is pretty crazy so basically yeah basically the whole concept is we&apos;re using a GitHub repo which then um host our podcast which is a compile flutter application to GitHub Pages which is free and we have an RSS feed which is inside of there as well as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:37&lt;/strong&gt; · each of the episodes are small enough um since they&apos;re you know rendered MP3 can be stored inside of the web folder and then when you&apos;re doing this and you compile it out you can actually static link to each page and each podcast episode and it works great our episode&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:52&lt;/strong&gt; · is now on you know iTunes Google Play and Spotify and we&apos;re not paying anything we you can always pay for custom domain later but if you want to we&apos;ll have the links in the show notes but you can actually open up the podcast player and listen to each of our episodes this is actually running on flutter web using the latest audio players plugin to be able to allow you to play audio on the web which is super cool and you know just implements a bunch of um you know RSS parsing and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:16&lt;/strong&gt; · stuff like that which we are actually open sourcing so if it&apos;s not out by the time that you see this episode it will be in the future but we will open source the podcast player so that you guys can build your own podcast or just see how audio and flutter web works together yes and we&apos;re also probably um work on improving the podcast player and eventually have a full-blown thing which um is super cool to see and it&apos;s all for our web like it&apos;s all FL this podcast app could also work on mobile on desktop&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:48&lt;/strong&gt; · and in this case on the web yeah I so speaking of this I I have actually been doing flutter and audio for quite a while now I built this flutter midi plugin a while back and I think I think flutter is really cool for you know having this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:05&lt;/strong&gt; · interaction model where you can build this awesome UI but have the audio extracted out I don&apos;t know when I first started with flutter the first thought was like how cool would it be if you could build like a VST in flutter or build like an audio Daw in flutter because um are you familiar with those at all I haven&apos;t heard both those terms before so basically what a Daw is is a digital Workstation and uh VST is just an instrument that um runs um in a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:31&lt;/strong&gt; · desktop form it&apos;s usually built on top of um C++ or something else and it&apos;s just a synthesizer basically but what&apos;s cool about that is they a lot of those have really bad UI and they&apos;re usually really old what&apos;s cool about flutter is we can write our UI in a way that doesn&apos;t depend on whatever platform it&apos;s running on and have the audio context be the backing for that plug-in um for example you can actually use flutter and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:57&lt;/strong&gt; · audio with rust and there&apos;s a really cool package out there for rust it&apos;s like the best one and you can just call into it with ffi you can play any kind of sound object you can control the length and it&apos;s really neat there&apos;s the audio players package and you know of course it&apos;s you know evolving and growing all the time but I I&apos;ve just been really fascinated with how powerful flutter is when it comes to you know not&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:23&lt;/strong&gt; · because the framework and the UI is not the same and because you don&apos;t have to be on iOS and have to map to iOS widgets because it&apos;s that case you could build UI that runs on Raspberry Pi for example and I think that&apos;s really cool because you can just focus on building the UI first and then add in the functionality as you need depending on where it&apos;s running yes wow that sounds super interesting and Powerful yeah are you planning to work&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:51&lt;/strong&gt; · on something like this in the future I&apos;m working on flushing out this midi plugin for flutter I want to have it as a full scale um you know in terms of feature par with other midi plugins for example like right now I use it for the the flutter piano that I created but uh when it comes to MIDI and flutter there&apos;s so many things that you have to do like not only do you parse the midi or play individual notes you then also have to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:18&lt;/strong&gt; · control like the sound of the synthesizer which synthesizer it&apos;s using all the different channels you can have a polyphonic synth a regular synth um as well as sometimes you just want to sample and it&apos;s like it&apos;s a NeverEnding project but one thing I&apos;m looking into is building the core part of it and rust because there&apos;s a lot of really cool midi and audio plugins compile that down to um I guess it would be WM for web and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:44&lt;/strong&gt; · then we would probably just use a regular uh shared Dynamic library for ffi and then my plugin would just wrap around that what&apos;s interesting is um it&apos;s not really you know in the the dart land it&apos;s all in the Audio I mean it&apos;s all in the native land but what&apos;s what&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:04&lt;/strong&gt; · neat is especially with dart you can actually generate ffi bindings and do all this different layer to where the user may not even know or care where the library is written um but for me it allows me to only have to write the plugin once and rust and of course I&apos;ve&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:20&lt;/strong&gt; · been also having to learn rust as part of this process so I&apos;ve been going through the Rust book and that&apos;s been uh kind of crazy because it&apos;s such a low-level language and you know having to learn about what pointers are and how it all works together and it&apos;s pretty insane but I got to say like the book for rust is one of the best books that I&apos;ve seen for documentation it is really really good um have you ever looked at it yes I&apos;ve actually looked at rust a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:49&lt;/strong&gt; · couple of months ago and read the book the first chapters and yeah it was a super super fun experience I I remember like like yeah like rust is such an interesting language it it combines this lowlevel um thing with a very uh Dynamic&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:06&lt;/strong&gt; · and high level language like you have all these things that um a modern a new language uh would have but you also have those pointers and all that stuff but yeah it&apos;s also super like the concept is quite different with borrow pointers and the compilers just wrecking your program every time you do something but once program is running it is running yes yeah it&apos;s uh it takes a while to get the first thing to compile but once you&apos;re there it&apos;s like yay and uh but it&apos;s it&apos;s a cool approach&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:38&lt;/strong&gt; · to programming but it&apos;s also can be frustrating when you just want to get something up on the screen and it&apos;s not working yes it feels like uh back in the days when I first started programming and it just didn&apos;t compile was like oh my God what is happening and I&apos;m like of course it doesn&apos;t compile the syntax isn&apos;t wrong uh the dynamic type or the the generic type isn&apos;t working but with Russ it&apos;s like what does that mean yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:03&lt;/strong&gt; · let me go pop out my textbook yeah yes yeah it&apos;s it&apos;s insane have you um used ffi with flutter yet like have what&apos;s your experience with been with that yes I&apos;ve used it and pretty great actually like um it took me a bit to figure out how to use rust and compile to the C ABI and do all those things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:26&lt;/strong&gt; · that they don&apos;t mangle the file names and don&apos;t remove stuff but once I figured it out and it actually wasn&apos;t that hard it worked pretty well um what I&apos;m still um struggling with when I use this and I still have to learn and look into is all those type conversions like just going from Dart types to rust types&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:45&lt;/strong&gt; · like just going from a string from dartland to rustland isn&apos;t that uh easy because well how is the string encoded how where&apos;s the pointer stored who&apos;s freeing it who&apos;s who&apos;s taking care of it it&apos;s like all those things you&apos;ve never thought about in a language like Dart you now have to think about yeah especially like the first time I worked with C++ I was like wait you mean string is just not like a standard lib and it&apos;s just an array of characters you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:13&lt;/strong&gt; · like it&apos;s crazy and then you start to think about like think about all the times in your code which like for me I naively just concatenated a string instead of using a string Builder and if you&apos;re not careful under the hood you know that&apos;s just like a really bad use case for you know having to reallocate an array size which can in binary can be&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:34&lt;/strong&gt; · really bad because it&apos;s a you know oin operation but then if you use a string Builder it&apos;s a whole lot easier to then and memory efficient to be able to concatenate strings and that&apos;s why it&apos;s always recommended but yeah I think there was a dart summer of code project that was out for specifically targeting you know generating this um conversion so you can take a header in and it&apos;ll generate all the ffi bindings for um converting to Dart objects if I&apos;m correct yeah yes I&apos;ve seen it I&apos;m looking forward to whoever&apos;s doing that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:05&lt;/strong&gt; · so yeah if you&apos;re listening we have you have our support so yes yeah I I am pretty excited to see all the stuff happening with it cuz I mean when you do the performance comparisons between ffi and a platform Channel even the binary platform Channel it is incredible how fast it is just to call right into ffi to just call right into into the lib and I don&apos;t know I&apos;m pretty excited about that and even I don&apos;t know if you&apos;ve worked with this yet but like you can actually compile your R code for WM and you know since&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:35&lt;/strong&gt; · flutter web supports anything that can run on the web including HTML and JJs web assembly is officially supporting the browser you can just call right into that WM library and um I built an example which will link in the description but you can actually I built the counter application but the counting computation is actually done in Rust which is compiled into WM and it&apos;s incredibly fast so I&apos;m excited to see what kind of stuff you can do there because you could build all kind of stuff you could build like a 3D program you could have a compiler you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:05&lt;/strong&gt; · could do whatever you want because web assembly is uh pretty incredible possibilities are endless yeah absolutely so uh what do you working on at the moment um right now like last week in the episode I talked about how I wanted to get back into things online again blog post and all that stuff I don&apos;t want to have something concrete just yet but uh let&apos;s say like this I&apos;m working on stuff and there&apos;s going to be stuff from me in the near future um what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:31&lt;/strong&gt; · about you yeah for me I&apos;m continuing to make progress on the flutter editor I have had such a hard time getting my sidecar for Tesla app released to iOS I have tried so many times Apple keeps rejecting it because my name they&apos;re trying to say is uh intellectural property so they&apos;re trying to say that I don&apos;t have the rights to make a Tesla app so that&apos;s been fun I&apos;ve been trying to change the name and remove anything I possibly can so we&apos;ll see it&apos;s working great on Android though um but yeah I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:02&lt;/strong&gt; · actually have been thinking about releasing it for Mac OS first and then seeing if they if they accept that and then if they reject on iOS be like well you you approved it on Mac OS so we&apos;ll see um yeah and then also you know just uh getting all this podcast stuff set up&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:20&lt;/strong&gt; · and as well as just working on a ton of other various projects um continue to make videos like you said and um yeah it&apos;s been really fun I enjoy doing this podcast cuz we are able to take these ideas and stuff that we talk about and go in depth in a way that we can&apos;t really do in other forms like it&apos;s it&apos;s hard to go in- depth on a render objects in a 10-minute video so yeah it&apos;s it&apos;s cool to be able to talk about things here um so if you want to find us our&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:48&lt;/strong&gt; · link to the podcast is down below we have links to iTunes Google Play and Spotify so make sure to follow us on all those make sure to give us a you and look forward to the next episode thank you thank you&lt;/p&gt;
</content:encoded></item><item><title>Flutter Desktop/Web and VR</title><link>https://rodydavis.com/podcast/creative-engineering/flutter-desktopweb-and-vr/</link><guid isPermaLink="true">https://rodydavis.com/podcast/creative-engineering/flutter-desktopweb-and-vr/</guid><description>This discussion explores the future potential of Flutter development, covering advanced topics including VR, code generation, and cross-platform capabilities for desktop and web.</description><pubDate>Wed, 25 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Flutter Desktop/Web and VR&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;nyxBRXjONoo&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Looking into the future of Flutter development, our experiments with VR, our thoughts on Code Generation and passion about new tech.&lt;/p&gt;
&lt;p&gt;Norbert Kozsir - @norbertkozsir&lt;/p&gt;
&lt;p&gt;https://twitter.com/norbertkozsir&lt;/p&gt;
&lt;p&gt;https://github.com/norbert515&lt;/p&gt;
&lt;p&gt;Rody Davis - @rodydavis&lt;/p&gt;
&lt;p&gt;https://twitter.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://github.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://youtube.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://rodydavis.com&lt;/p&gt;
&lt;p&gt;Our podcast player:&lt;/p&gt;
&lt;p&gt;https://rodydavis.github.io/creative_engineering/
Follow on Twitter:&lt;/p&gt;
&lt;p&gt;https://twitter.com/CreativeEngShow&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · welcome to creative engineering this is our first podcast on all things creative and open source projects what we&apos;re working on and what we&apos;re excited about with me today I have my co-host sbert how about you tell us a little bit about yourself thanks Ro um my name is sbert um I&apos;m studying computer science in Germany been doing FL for about 2 years now and always been interested in pushing the technology thinking about our ways to do it and just trying things out right now unfortunately um having a lot of work so I&apos;ll right now in a phase&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · of trying to get back into making stuff happen and doing cool stuff how about you roie yeah so I have uh been working with f for quite a while now um before this I had a non-traditional background I was a musician worked as a audio engineer for a number of years system administrator and just one day decided I wanted to explore creative passion again and that led me to software development specifically development uh where I built my first app that got me my first&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04&lt;/strong&gt; · job and it was um really challenging but at the same time I had this unrelenting passion for creating things because I realized that app development was just this blank canvas um and was super excited about it so what uh Got You Started With flutter um it&apos;s quite a fun&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · story well it&apos;s not that fun to be honest but basic was a sheer accident like I was sitting in my computer one day and we just scrolling YouTube as you do and I saw this video I think was from the dark con um and they talked about something called Flor and never heard about it um I actually haven&apos;t watched the video but like the next day I came&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45&lt;/strong&gt; · back and was like oh that sounded interesting let&apos;s let&apos;s just take a look uh I watched and was like super interested back at that point I was working or just as a hobby project on an app in Android um like I&apos;ve been doing Android before Flor and basically the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:03&lt;/strong&gt; · app was a book collection app like at that time I was reading a lot I thought how cool would it be if you could um some somehow gamify reading books collect those books and just have your virtual um bookshelf and so I thought to myself okay flourish sounds pretty interesting um could I try this out and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:25&lt;/strong&gt; · see how it goes in Flor um so I downloaded the SDK um I installed it I never heard about Dart before um but was actually very easy to get into like Dart is very similar to Java in cotland which I&apos;ve been doing before and I&apos;ve literally coded the app from from first&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:45&lt;/strong&gt; · hearing about Flur to having a very basic MVP like it was very basic they had a search bar and you could Quarry an API um but I had to done in about one or two days and it felt great um and soon&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:00&lt;/strong&gt; · after I actually like back when I was doing Android I always thought about making a Blog and I was like what should I talk about so with Flor I decided okay I made this little La why not um make a Blog about it and that was my very first blog post and from there on it just more blog post most more stuff um yeah wow&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:22&lt;/strong&gt; · that&apos;s awesome yeah I feel like a lot of people um have a similar experience with flutter where Dart was not really the limiting factor and also I feel like a lot of people give Dart a bad name because they think of Dart one and it&apos;s by far not the same Dart um for me I had&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:38&lt;/strong&gt; · a pretty similar experience because I was doing a lot of c.net development um as well as I also did Android but before Android my experience was actually in Swift and um Objective C building iOS apps but then I came across uh this platform called flutter when I was doing looking for a crossplatform solution I had looked into I actually created examples in Native script zamarin react&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:04&lt;/strong&gt; · native and then this was just when flutter beta 2 launched and I was like sure I&apos;ll give it a try and then I was blown away with how stable quote unquote uh the Beta release was of this framework and how it was so much more mature than um a lot of these other Frameworks out there that had been out for so much longer and I love the fact that you could use this SDK and update&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:28&lt;/strong&gt; · to you know version and not have to break everything yes there are going to be things that didn&apos;t work but it was like you know changing usually a variable here and there and um I was just really happy with the developer experience side of flutter yes also you briefly mentioned Dart not being a limited factor um to be honest the more&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:49&lt;/strong&gt; · I work with dart and the more I look at our languages I feel like Dart right now to be honest for mobile development is one of of the best languages in my opinion because it combines so many powerful things like okay just in time completion ahead of time compilation which is like one of the big things but even small language features like always&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:13&lt;/strong&gt; · adding the the spread operator the there&apos;s so many other things which just make Dart a very expressive and a very fast to write in language like I rarely have times where I sit down have a problem I think okay Dart is actually holding me back in doing this which I had in Java like if I remember back just having to Define new classes to have a callback having all those things before the L lambdas came in uh I think it was Java 8 wasn&apos;t it yeah like d yeah yeah Dart feels like a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:48&lt;/strong&gt; · good language and it somehow combines the power of having those untyped JavaScript things and just being fast but also having the the Java and the strongly typed things to make things productive and bug free and also um yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:06&lt;/strong&gt; · you touched on the the hot reload that to me was just something I was so excited about you know like when I was working with Firebase on iOS I mean you know you&apos;re talking about like eight minute build time sometimes and it&apos;s like you know like oh I&apos;m gonna go adjust something in my storyboard oh let me move it by half a pixel oh still wrong let me go compile again eight minutes you know and just this like weird infinite Loop that you get in um and for me I don&apos;t know if it was the same for you but I was always afraid of UI development before flutter I had done&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:38&lt;/strong&gt; · a lot of UI development but I just thought it was like a bad UI designer but what I realized is I didn&apos;t have a platform in which I was able to create um and express myself while experimenting and not being afraid to try something with flutter like I could prototype four or five different screen uis in the course of like 15 minutes that would take me me you know easily a couple days to do an iOS um they&apos;re just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:03&lt;/strong&gt; · and also at least with storyboards and declarative UI you put so much into the UI that there&apos;s almost like this um you know this sunk cost fallacy where you feel like you&apos;ve put so much work into it and you just don&apos;t want to break it you just want to make sure it stays working or or you do the inverse where you just find components and be like oh this is the stock components they look good so might as well use them I was guilty of that because it&apos;s just like well I can use the table view here I can use the navig component here I can use you know um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:34&lt;/strong&gt; · these you know just all the default stuff and I felt like that really limited my UI because you know it was only ever as good as the OS was going to make it yes and also like just not being afraid of doing something on your own like an Android and stuff if you see a designer try out something new and just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:53&lt;/strong&gt; · pushing the boundar is was like oh my God that&apos;s so hard like I have to read so much documentation and figure out how to operating systems handling this and flor&apos;s like okay so we could use a hero widget we could also look into how the hero widget works and just build something from there like you have all the expressivity like you can build so many custom things um without much of a hassle like like most of the things that are done are super easy to build the only&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:23&lt;/strong&gt; · thing I&apos;m a bit afraid of in Flor are slivers because slivers are always super complex um and they&apos;re they&apos;re sometimes weird but even like they they also have a lot of components which you can just use out of the box and at the end you can make something work but other than that like all the things are just so easy to use if you want to make some custom painting or have some sort of graphic it&apos;s like super easy to do that with flutter well what&apos;s nice about flutter to is you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:53&lt;/strong&gt; · take you know an existing you know render box for example you could write your own layout strategy if you want wanted to um you could completely um you know follow CSS grid um just with pure You&apos; have to implement all the the layout Behavior but you know flutter was the first framework that allows you to truly you know set your own behavior you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:14&lt;/strong&gt; · can have it as abstract or as down to low level as you want you could go straight to the canvas and just paint everything custom or you could just rely on pre-built widgets like containers and all this other stuff and you can still get a really cool performant UI um yeah there&apos;s some things you got to watch out for um that you need to be careful of but overall I mean I feel like you just really can make expressive UI fast yes and I mean combining that with hot Tre like it sounds dumb but just having that hot reload makes it so much more fun and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:46&lt;/strong&gt; · like you&apos;re more happy to develop with hot tro like I feel like that it&apos;s the most frustrating thing ever if you have a um bug which might not be that big of a bug but you have to try a few things out like maybe you have a wrong path and you have to see where it&apos;s a relative one or whatever and build times are high&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:05&lt;/strong&gt; · like it&apos;s so frustrating to change something little and then wait for literally 5 minutes until you can see okay it didn&apos;t work let&apos;s try something different and it just eats up so much time but also energy like you&apos;re not actually working but it feels like hard work compared to just making UI hot reloading and just going through that blazingly fast yeah and also you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:31&lt;/strong&gt; · you you have this hot reload cycle but it also keeps the state of the app so you can be you know 20 screens deep and you&apos;re just updating that change which is awesome um and speaking of hot reload being fast uh what your experience been like with working with floter on the desktop um as a prototyping tool yes so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:51&lt;/strong&gt; · that&apos;s actually one thing if somebody ask for my secret tip and fur is if you have an app which doesn&apos;t use too many many plugins which are maybe not implemented desktop go give FL desktop a try and see how it may improve development like it has so many benefits like for example if you have an application which doesn&apos;t use any plugins so everything works for a desktop first off resources like my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:17&lt;/strong&gt; · Hardware isn&apos;t the best 8 GB of RAM isn&apos;t that fun to use intell J with an Android emulator um so instead of using a real device just booting up Flor deskt top uh running it in there is such an improvement also like just being able to resize the window and see different configurations is so great um how&apos;s your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:41&lt;/strong&gt; · experiences yeah I I have the same um you know I love being able to prototype UI I find layout bugs in just a matter of seconds on desktop where that takes me you know months to figure out oh I built my app but let me test it on the iPhone 5 oh everything&apos;s broken so it&apos;s like you know it&apos;s like uh with desktop um I don&apos;t know if a lot of people know this but you know you can you can test web UI on desktop because you can test&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:09&lt;/strong&gt; · all your responsiveness and it&apos;ll be the same UI when you launch it on the web so um I don&apos;t think web supports hot reload at the time of the recording this episode but um yeah it&apos;s been super nice to be able to do that and also you&apos;ll be surprised at how many plugins do work I mean Firebase authentication Cloud functions and a bunch of other Google signning even I believe just recently got support so all these things you can actually do on the desktop already um and yeah and it&apos;s not too bad to do API calls anyway so it it works on Mac most&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:41&lt;/strong&gt; · like I think all those cool things that work work on Mac so um which doesn&apos;t help me too much to be honest for me it helps yeah it&apos;s true like the biggest thing right now with our desktop is that it doesn&apos;t support Firebase like that&apos;s pretty annoying because I had and have a few projects with Firebase which I&apos;d love to use for a desktop to develop and prototype with but I just can&apos;t because most of the app relies on on Firebase and I mean I could&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:11&lt;/strong&gt; · Implement some sort of mock of Firebase on desktop but that wouldn&apos;t like that would be too much work for um yeah that yeah yeah I mean there are some there&apos;s a cool package out there which we can always link to in the description which is called fire Dart which is a dart native uh compatible implementation of Firebase it&apos;s all in pure Dart and it basically wraps the grpc kind of SDK um what&apos;s nice about this is like I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:39&lt;/strong&gt; · have built a couple admin utilities so I can like create Crown jobs that like update fir store documents things that aren&apos;t necessarily a good fit for functions but are great fit for like you know these onetime content updating um operations so what&apos;s neat about that is that actually already does work on desktop and doesn&apos;t depend on anything else you just have to pass in you know the the uh you know the the secrets to make it work yep um so what um you know one of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:11&lt;/strong&gt; · one part of this podcast is we we love to explore all kind of things that are you know flutter is one thing that we are both incredibly excited about but we also are really involved in a lot of other projects and excited about General Technologies and um other projects and apis that we&apos;re familiar with so um what are some experimental projects that you&apos;re working on that uh have gotten you excited over the you know past couple months yes so as of right now I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:42&lt;/strong&gt; · prepacked with work so my free time for experimental projects have has sunk quite a lot which I hope uh will come back soon um but what I&apos;m always very interested in like one thing that stands out to me and I think um I I&apos;m not the only one who likes um building de developer tools like we as developers use stuff and see what&apos;s what&apos;s slow&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:08&lt;/strong&gt; · what what could be better and just building stuff which makes your own workflow better is so fun and I was always thinking about um how could you make for instance in this case FL development faster and um yeah just just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:25&lt;/strong&gt; · fast like I was always thought like if you have an app idea and you know what it&apos;s going to be like you know you you will have to log in you have account settings all that stuff why does it still take you a couple of weeks or even months to build that app why can&apos;t you just sit down um maybe take some designs which you bought maybe you have a designer and just finish the app in a weekend um so one big project which I&apos;ve been working a lot on last year mostly&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:54&lt;/strong&gt; · um is the widget maker which gun has gone through so many iterations and things um project as of right now is on hold for just a bit um working on something else um which is pretty similar but um yeah just just making um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:14&lt;/strong&gt; · developer tools is very interesting right now how about you roie so real quick what what is the widget maker for those that are curious oh that&apos;s that&apos;s a very good point I didn&apos;t mention that um so the widget maker the original idea was um a lot of languages have or a lot of UI Frameworks have those uh dragon drop Builders which basically allows you to drag uh in your elements composite them and then have a finished running app um the in the difference about the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:46&lt;/strong&gt; · widget maker and the original concept is um those R um UI bus rely on a very static representation of UI think of XML in Android or HTM L and CSS on web but Flur is different thur has this code which is very in a sense organic is very chaotic and it can be very very hard to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:12&lt;/strong&gt; · put into a structural form like yeah in in some language like HTML you have like one or two or three ways to do something in Dart and Flur you have infinite ways like you don&apos;t have to return a widget you can return a function invocation which returns a widget which is totally the same yeah but it&apos;s still different&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:34&lt;/strong&gt; · and people might be doing that because they have some sort of clever ideas how to abstract something away and the problem is you don&apos;t want to limit people in their creativeness like okay you cannot do this because you&apos;re using this but um it&apos;s also very hard to work&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:51&lt;/strong&gt; · with such code and that&apos;s one thing I&apos;ve always been trying to work around with um but yeah that&apos;s the widget maker nutshell wow that&apos;s awesome yeah I have I know I remember first seeing it on Twitter and then being able to use it and I thought it was just so cool to be able to like prototype a screen or even just a widget um because for me I have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:16&lt;/strong&gt; · this dream that I I hopefully will realize someday is um you know be able to build a flutter project on my iPad you know be able to Proto prototype it and do everything because I feel like when you&apos;re in the prototyping phase the design phase that should you don&apos;t need a compiler for that you don&apos;t need that yet uh but once you get to that point you want to have a you know robust ready too project so I don&apos;t know I&apos;m still exploring it with um my flutter editor that I&apos;ve been building but you know it&apos;s um it&apos;s it&apos;s a cool concept because&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:50&lt;/strong&gt; · flutter I love that it doesn&apos;t limit you in that way like you it&apos;s not like on Android where you just have XML like you said or you know you can you can really it&apos;s a declarative way but it&apos;s also it&apos;s whatever you decide is the best way to generate now that means that there&apos;s some best practices like for example if you&apos;re using a function to return a widget sometimes it&apos;s not always recommended but um one of the member in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:17&lt;/strong&gt; · the community named Remy he&apos;s made a awesome package called functional widget which you can actually annotate those functions and it generates the actual implementation under the hood um speaking of which that brings me to one of my kind of um things that I&apos;m exploring pretty heavily is um code generation and flutter because uh flutter has been the first tool that I&apos;ve really used to do code generation hardcore and the fact that you can just like you know with a couple lines of code generate you know thousands of lines of everything you possibly need um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:50&lt;/strong&gt; · is super cool um one thing one use case for this that people are unaware is like you can very easily take a data class and and and dart for example annotate a couple fields and then have Json serialization um just out of the box you can uh there&apos;s a package called more m o r um which is super cool where you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:12&lt;/strong&gt; · annotate tables and uh generate a whole SQL light database just with code generation and not have to write SQL statements themselves so um there&apos;s a lot of places in which you know flutter and dart in general allows you to to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:28&lt;/strong&gt; · write code that writes more code and and I think that&apos;s pretty cool um some experimental projects I&apos;m working on are um I&apos;ve been exploring rust uh with flutter uh I think rust is an awesome language been going through the Rust book and I think it&apos;s incredible just uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:46&lt;/strong&gt; · how well it you know the first of all how well the book reads but how cool it is to be able to write like an audio library and then have it compile to wherever you need um so I&apos;ve been exploring creating like WM R Wy wrappers really um using wer and then calling into that with ffi ffi is a really cool&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:06&lt;/strong&gt; · technology in Dart which allows you to call into any existing C and C++ code or library and yeah I think I think it&apos;s awesome have you have you worked with ffi yet yes I actually have I&apos;ve also explored using rust on um FL especially for the desktop um one thing that&apos;s super interesting about this fi rust&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:26&lt;/strong&gt; · thing is that those libraries you build if you use uh a library which is implemented on all operating systems or just has some functionality which works every there it can work on every platform like you can compile to all desktop platforms but also mobile so as you said you can have a an audio plugin&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:46&lt;/strong&gt; · which just works in all platforms and you don&apos;t have to implement the other side like the native side multiple times you can just have one implementation have one repper for that and just have it work on all platforms which is super powerful the biggest thing I&apos;ve seen with that though is um a lot of the ones that work on all the desktop platforms usually don&apos;t work on web and the ones that work on web don&apos;t usually work on desktop at least for the audio stuff has that been the same for you haven&apos;t been exploring web with uh ffi too much to be honest I mean uh rust and web okay so I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:17&lt;/strong&gt; · haven&apos;t uh done anything with Rus and web yet um can you sh a bit about your experiences because that&apos;s still very new to me the whole um web assembly and rust on the web so the cool thing about web assembly if people aren&apos;t aware it&apos;s a it&apos;s a nice binary interchange format that allows you to easily load these modules um and run and execute code in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:40&lt;/strong&gt; · the browser not in JavaScript but directly in the binary format so um one cool thing about that is Russ can compile to web assembly as a Target um on on mobile and desktop you can use ffi ffi doesn&apos;t exist on flutter web so to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:57&lt;/strong&gt; · get around that you can actually use the web assembly Hooks and flutter um which are part of the built-in Dom um you know just part of the regular HTML Library you can call into web assembly and it&apos;ll call a code I have some examples on GitHub that that kind of dive into this but it&apos;s really cool because you can use the same rust code um if it&apos;s a generic&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:19&lt;/strong&gt; · enough code like a you know like kind of um like a math library or something like that the cool part is Russ will compile to all the targets pretty easily um if you&apos;re dealing with things that deal with like file AO and stuff like that then it&apos;s only going to support a limited number of targets but what&apos;s cool about that is um you know you can like for the counter example in flutter I was able to write the counter logic in Rust compile that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:47&lt;/strong&gt; · to web assembly and then use wer to run web assembly in flutter on all the platforms so since W wzy if you&apos;re unaware is the wrapper around web assembly that allows you to run web assembly on desktop mobile and web on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:04&lt;/strong&gt; · web you don&apos;t have to have it wrapped since you can just call directly into it um but that&apos;s really cool I think that&apos;s a an exciting feature for um sharable code because what&apos;s neat about that is like think about like all the packages we create and what if we wanted to create a native plugin and instead of doing it in Dart or if Dart supports web assembly we could write the the native code have it compiled to web assembly and then the plugin would just be the wrapper around that web assembly module so instead of having to write you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:34&lt;/strong&gt; · C++ and Linux or sorry C++ and Swift and Objective C and C or we could also just write a plug-in that calls the ffi handles and stuff like that so I think that&apos;s a a nice interesting wave of the future of plug-in development too yes definitely one thing I also wanted to talk about quickly you mention a few things uh before we switch to the native topic um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:03&lt;/strong&gt; · first of code generation like one thing that I&apos;m always um curious um what people think is so for me code generation is this weird thing it&apos;s super powerful and it can do so many amazing things but I don&apos;t really like having to generate code to write valid code like for example um Jason parsing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:25&lt;/strong&gt; · like I first have to write code which doesn&apos;t compile on its own because it depends on part files and some things that&apos;s are going to be generated later and you have to actively run the generator or start the Watcher which does it all for yourself um and you have the state where&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:42&lt;/strong&gt; · the code is broken until you run this command and it somehow adds more mental complexity for me like you have this things you you cannot touch and I I don&apos;t like it too much what is your opinion so so I&apos;ve taken a different&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:59&lt;/strong&gt; · approach which is like I&apos;ve been a super huge fan of freezed um because I like anything that&apos;s I&apos;m generating with code is almost always an immutable data structure something that&apos;s not going to be modified or changed um in fact it won&apos;t be changing at runtime and it&apos;ll always be whatever I release it with um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:20&lt;/strong&gt; · so from that standpoint yeah it it is kind of weird sometimes when you&apos;re like in this Loop of like make you a change and then you run the generator and see what broke because it may update a lot of different things that don&apos;t exist anymore I had that big problem with more for example like I&apos;d update a SQL statement or you know whatever and then all of a sudden you know three different screens have to return a different object so that was kind of interesting&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:47&lt;/strong&gt; · um to deal with but I would say for the most part the benefits of outweigh the cons for me yeah like I still hope like I love coach generation and um it&apos;s great because it just adds this new layer of metup programming which exponentially increases the power you have um but yeah I as somehow hope there&apos;s going to be some sort of solution which is more which feels more natural and is easier to to use like I I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:18&lt;/strong&gt; · haven&apos;t used code generation just recently but I remember using those Json sterilizers and each time I wanted to use them I had to go for a tutorial because I realiz okay now I have to add this serializer and I have to import this part file andg files and was always a bit complex and I I really hope it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:40&lt;/strong&gt; · going to be uh easier at some point well I do know that there there is some work being done on flutter like there is a flutter generate command which does kind of it takes that abstraction instead of generating the code you know right along with part files it generates the code inside the build folder so as a build Target so I don&apos;t know if that would be helpful because I mean it is helpful but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:05&lt;/strong&gt; · I don&apos;t know if that&apos;s maybe would be easier for you because then it&apos;s more of a you have everything you need because you&apos;re declaring it um and then it basically generates it the implementation like you&apos;re working with an abstract class and it generates the hardcoded class if that makes sense that&apos;s interesting yeah because yeah also as you mentioned like where the generative files are like it&apos;s always super not confus using um but it&apos;s like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:29&lt;/strong&gt; · all those fils which you don&apos;t want to look into and don&apos;t want to touch um and you&apos;re thinking should I commit this to repository should I have other people generate them themselves and it&apos;s always like this a bit of a hassle you know the uh the latest recommendation on the flutter team and the dart laying is that you do check in generated files now when I first started with flutter it was not the case but um they have since changed it to where it is recommended which is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:54&lt;/strong&gt; · kind of cool so like you know if you&apos;re working collaborating on Project like uh have a project called amp store we you know I&apos;ll out of you know Simplicity I will go ahead and generate all the files and then if someone needs a change then I can update it but yeah for the most part uh only usually one or two people are generating the new files because in theory you&apos;re wanting to generate as little as possible um like you don&apos;t you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:22&lt;/strong&gt; · don&apos;t want to just be like you know generate application and then all of your application is gener because it&apos;s like uh while they would be great and we wouldn&apos;t have jobs that would be uh you know it&apos;s it&apos;s you know you want to there&apos;s this flexibility that you or um this dance that you have to make you know where you know you have this tradeoff of generating code to save you time and generating and writing code to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:48&lt;/strong&gt; · be flexible and there&apos;s this balance that you have to strike and it just really depends on the project for sure yes definitely so um another thing that we both have really gotten excited about is using the Oculus Quest and at flutter interact we were both able to try this I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:06&lt;/strong&gt; · was able to bring my headset to flutter interact and I just was curious about what was your thoughts about you know trying VR for the first time and what&apos;s got you excited about you know just the general scope of VR in general um actually that was my first time trying VR like before that I used to um go to a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:26&lt;/strong&gt; · So-Cal VR space my city a couple of times um they have those HDC vies um basically for stations you pay for 30 minutes and you can have up to four players and play some games um but the thing that was so that stood out so much to me when you brought a VR headset is how accurate that was compared to the HTC which HTC is perfect and the ocus is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:51&lt;/strong&gt; · also perfect but at a significantly lower price point and without cables and setup like I remember we were at the Airbnb we had a couple of hours left and we just took the Oculus Quest I I drew&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:08&lt;/strong&gt; · the whole Airbnb on the ground and was able to navigate through that without colliding with any objects and just feeling confident because it didn&apos;t lag it didn&apos;t have some sort of weird artifacts it just worked and at that point I got so excited because just being free like instead instead of being bound to a computer with cables and stuff you can have this headset take it on wherever you are and have all those&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:35&lt;/strong&gt; · um very interesting immersive experiences yeah um I feel like for me it was I tried it in a Best Buy it was the first time I ever tried VR and it was an Oculus Quest yeah I believe the time and I just remember taking off the demo and being like I cannot leave the store without this because it was just like it had completely changed my mind I had used Google Daydream I had been using Google cardboard since it came out I had been using pretty much any um access&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:06&lt;/strong&gt; · besides using like the rift and you know Oculus uh Oculus Rift and ht5 yeah I um you know I was just I was so fascinated with mobile VR but then the Oculus Quest was the perfect balance I was looking for which was the flexibility of desktop&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:22&lt;/strong&gt; · VR with the mob ability of you know mobile VR and the fact for me was the hand controllers you know being able to like just reach out and I remember playing the SP P space pirates demo and just being like looking at my hand which had a gun in it and being like how is it so detailed I could just look around and I was like it looks like it&apos;s right there and your brain is like freaking out and be like and then also there&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:47&lt;/strong&gt; · this app called you know the plank experience and you could just walk out and I just everything in your mind is like trying to convince you that this thing is reality and it&apos;s uh it&apos;s incredible the technology and how far it&apos;s come I haven&apos;t actually tried that one yet like the pl experience a lot of people told me about it um I&apos;ll definitely have to try it at some point I remember when I very first tried VR I think it was actually on Gamescom like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:13&lt;/strong&gt; · quite a couple of years ago when the how was the first headset called uh which was by Facebook uh oh go I think you talking about the same I think it was it was one of the very first ones I think it was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:29&lt;/strong&gt; · was their main one I&apos;m not quite sure how it was called what but basically it was a sitting experience like uh being at Gamescom just waiting all day to get in and I remember playing this game where you were were in this uh space suit or whatever and you could jump around onto high buildings and jump down and jumping down from that building actually felt like falling even though I was sitting have you got the update for hand tracking yet um yes um I do like I I think there were&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:02&lt;/strong&gt; · a few R features um I&apos;m not sure if I have the big new update I think that also has a few UI changes but I definitely feel like the hand tracking got way better and yeah talking about hand trkking like that&apos;s the next step like those controllers are pretty cool like but it&apos;s still like you have to use the controllers you have to get into into those straps um you have to have your hands it depends on game but just having your fingers and having all this control that&apos;s I mean it opens so many&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:34&lt;/strong&gt; · more possibilities uh what have you tried with hand tring like what is your experience so my favorite uh latest Point update was the fact that it can automatically switch between hands and controllers based on interaction which is so awesome like so for example like a normal day in VR I may pop on my headset&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:52&lt;/strong&gt; · open big screen maybe watch a movie and it&apos;s just that&apos;s an interaction where I just need my hands but then like I want to pop out beats saber that wouldn&apos;t be kind of awkward with hands but it works great with the controllers and then I go over to Robo recall and I bring out the controllers but then I want to go back to like um you know YouTube for example or Netflix and it&apos;s like hand controllers or just using your hands is nice yeah but do those apps work with fans like when uh I tried uh the hand&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:18&lt;/strong&gt; · tring a couple of times and most apps didn&apos;t support hand tring even though they didn&apos;t actually need controllers that much do it doesn&apos;t nflix and all those apps support hand treking the only one I&apos;ve used so far that supports it really well is the Oculus TV app so yeah um yeah because a lot of the developers need to update their apps to support this new feature um but in what I suspect is maybe we&apos;ll see a um I think&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:46&lt;/strong&gt; · we&apos;re going to see a compatibility mode that can be turned on in settings allows you to use hand tracking in any app so we&apos;ll see So speaking of VR how do you feel about developing for VR because I feel like once I started using it I got really excited about VR but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:05&lt;/strong&gt; · there was just something in me I was like I have to create something on this platform because it was just like app development&apos;s really fun desktop&apos;s really fun but like there&apos;s something about being immersed in a 360 environment putting anything where you want and just I don&apos;t know like what&apos;s your thoughts on that because I I just think that&apos;s like the next wave of application yes like actually the main reason why I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:31&lt;/strong&gt; · bought that Oculus class like I tried out with you I think I literally bought the quest like a few hours after um yeah it was yeah was basically what you can create with it like I I know VR I used VR before um I know the feeling it&apos;s great and having buying something like this for that price I think was like 300 bucks which compared to the other uh the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:57&lt;/strong&gt; · r VR headsets is like super cheap because for those you need the headset you need all the equipment and you need a good gaming PC but with the quest like just being able to um move around free like I thought about so many ideas you could Implement like just think about it you&apos;re not bound to a PC so you can even&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:17&lt;/strong&gt; · though it would look very awkward you could take on and use the headset anywhere like I thought about how cool would it be if you&apos;d be in a car obviously not driving yourself but sitting in there as a passenger getting that headset on having some sort of location data and maybe acceleration data and just replacing the stuff you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:39&lt;/strong&gt; · see outside with VR stuff maybe you&apos;re in a race maybe you&apos;re just looking at beautiful landscapes yeah exactly yeah just just having the ability to move around freely and just not being restricted was like mindblowing to me for you yeah for me um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:58&lt;/strong&gt; · I have been experimenting with um you know uh there&apos;s this technology called Web XR um which has come out web VR has been out for a little bit now and web VR is the ability to do virtual y experiences on the web um web XR is that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:15&lt;/strong&gt; · um with a proper implementation and a proper web standard the difference is web XR can also Target augmented reality experiences so I&apos;ve been creating a bunch of demos recently and particularly I love this framework called A-frame which is built on top of 3js it allows you to declaratively build these scenes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:34&lt;/strong&gt; · um with HTML which is super cool or at least web components um and what I like about that is uh there&apos;s even a project called react 360 which is cool too where you can build you know websites in UI and you know VR kind of formats but I think it&apos;s so cool because you know when you&apos;re when you&apos;re developing in this you know experience you don&apos;t have to be Bound by just like a A 4x3 or 16x9 aspect ratio box you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:02&lt;/strong&gt; · know you can have all your important content in front of you you can have look around for you know other details you could literally build you know like a mission control room and have everything you need and what&apos;s cool about the the quest is you can literally walk over to anywhere you want and have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:22&lt;/strong&gt; · everything change like you know versus and interactive too versus you know in a 360 video it&apos;s all just passive and in a mobile application it&apos;s all about what&apos;s just on the screen at the same time but like it really and especially interactive movies and stuff like that allows you to like let the user choose what they want to do and you know how they want to see it yes also just just mixing that with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:48&lt;/strong&gt; · the topic we had before FL like um Chris Sals actually had um a very interesting uh device build which you called the FL octopus um basically it&apos;s like this thing which consists of eight arms um with each of those arms being connected to some sort of mobile device or desktop&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:11&lt;/strong&gt; · and just hot reloading and developing Flur on all devices at the same time and like with the Oculus Quest you could take this FLIR octopus to the next level instead of just having eight devices you can develop with like you could literally have 1,000 devices like just imagine having a big big house a flat having a wall putting on the VR&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:34&lt;/strong&gt; · headset and having all sorts of device configurations on that wall just walking around and interacting with the app and seeing how it works well I&apos;ll tell you what if I&apos;m uh developing with the flut octopus and um VR I&apos;m going to be doing it like X-Men and have like a sphere development room yeah it&apos;s um it&apos;s pretty crazy how&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:57&lt;/strong&gt; · how you&apos;re not limited anymore by just like this 2D space it&apos;s like it&apos;s literally mobile development going 3D and it&apos;s um because you know you still have menus and all these other stuff but you just you adapt it for virtual reality so you don&apos;t have a lot of flat panels you have curved panels and you don&apos;t have so much your depth is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:18&lt;/strong&gt; · literally depth so material design is really big on you know elevation so cool thing is that translates to VR because because you know a floating action button will actually be physically closer to you than it will be um and whatever and I think another cool thing you know octopus is one awesome thing to do in VR but another thing for mobile development and web design I think would be a cool way to explore it would be like what if you wanted to explore your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:44&lt;/strong&gt; · view hierarchy or your list of dependencies and node graph or like just like visualizing a data table schema you know it&apos;s just like you see things from that perspective you cannot see in in other ways it&apos;s interesting yeah like a lot of errors and a lot of um time you spend as a software engineer is just um coming up with Solutions fixing stuff and just visualizing stuff is so important in the whole process and just having this as you said this this another layer which helps you put things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:17&lt;/strong&gt; · into context and just iterate and move around like you could even combine this with some sort of collaboration like think about having some sort of room with a lot of devices um and you have your buddy with you who sits in her table in real life maybe on her side of the word and you&apos;re just both coding if he wants to ask you a question you just come over and look over his shoulder and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:42&lt;/strong&gt; · just point out okay look at this line maybe change this and you could go to his virtual keyboard and mouse and and fix it for him and then walk around then point on the device on the screen and just uh compile the app for that device like the possibilities&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:02&lt;/strong&gt; · are quite literally endless yeah and I feel like in a time like this I mean at the time of this recording we&apos;re going through the Corona virus and so it&apos;s like having to do everything from home and having you you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:17&lt;/strong&gt; · kind of really start to love the idea of virtual reality because I don&apos;t know about you but I I miss hanging out with people and it&apos;s like uh being able to you know connect with people in this virtual way not bound by anything like you know I don&apos;t have to fly to another country just to be able to collaborate with someone on a project yes you can do it over GitHub and poqu and screen sharing but what&apos;s cool about VR is you also have your avatar and it&apos;s just I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:44&lt;/strong&gt; · don&apos;t know about you but it&apos;s just like having a conversation VR is so much different than a FaceTime call or whatever because you just you have this non-verbal communication too that just you don&apos;t get in other platforms yeah I mean that&apos;s a big thing like that&apos;s I think why still not all work workers remote because that physical in-person um connection sometimes can help like I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:08&lt;/strong&gt; · mean remote works uh in a lot of cases but sometimes just having some way of actually working together um can make a difference the interesting thing I I thought about VR2 is right now with the Oculus quest in my opinion we&apos;re at a point technology wise where everything&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:28&lt;/strong&gt; · is is there what you need to build something like we have hand tracking you can send voice over over the internet you can move objects you can synchronize pixels like that&apos;s what it is devices you can send over keystrokes you can do whatever you want all you have to do right now is combine all those Technologies and build something new it kind of feels like the early days of the web like everything&apos;s possible it&apos;s all super experimental um but nothing from like from core&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:00&lt;/strong&gt; · technology is missing right now to build something cool yeah we&apos;re just limited by what we can create it&apos;s an interesting yeah it&apos;s it&apos;s no longer well we need more Hardware like the only thing that Hardware is helping with right now is machine learning yeah you know you want to have as crazi as powerful computer possible but all it does is just shave off time um until we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:24&lt;/strong&gt; · get to like you know a full like Quantum Computing state where you can um really start to do these mathematical problems that can exist in multiple States I I just feel like right now we&apos;re in a new wave of just like we&apos;re only limited by the things that we can create and how fast we can create them um which is pretty exciting time to be a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:49&lt;/strong&gt; · developer so uh this next section I want to have devoted to just any new packages that you have found in the last week or so um is there anything you&apos;d like to share that you&apos;ve come across that just you know super glad you found Yes actually it is not a package and it is not directly related FL um but I found this um GitHub project uh which is called screen copy like it&apos;s um Sr&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:19&lt;/strong&gt; · cpy um which basically allows to copy your phone screen onto your desktop um which is super cool if you have um less powerful Hardware like I do uh for desktop isn&apos;t working yet because I&apos;m using Firebase and windows um and the cool thing is I&apos;m using ADB over WiFi so what I can literally do is have my phone somewhere in the house compile to the phone and have the screen on my computer and just have an Android emulator which&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:50&lt;/strong&gt; · is not an emulator without the performance impact so does it remote debug too yes like every everything like you can hot reload you can have break points like it&apos;s literally um it&apos;s the same as if you have connected your phone with a cable um but it&apos;s over Wi-Fi and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:07&lt;/strong&gt; · you have your screen on your computer and the cool thing too is you can interact with the phone like you can use your keyboard you can um you can even drop an AP case into the window and have it installed in the device like I I just came across it like a couple of hours ago and I was like that is cool um yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:26&lt;/strong&gt; · so does it allow um like tapping on dragging around on the screen it all works it all works like you can use your you it&apos;s like I played around with it for about 10 minutes um but I&apos;m definitely going to use this for those times where FL desktop isn&apos;t working or my computer um would burn down if I started another emulator um but yeah like we&apos;ll have the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:51&lt;/strong&gt; · links to those in the description um so yeah absolutely check those out um but yeah how about you um for me the packages I&apos;ve been most excited about recently is the new freeze package um and the new state Notifier and flutter State Notifier these all work together in a really cool way um it allows me to have this form of State Management which is a a One Direction data flow um but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:17&lt;/strong&gt; · also allowing me to have take advantage of you know value Notifier um as well as you know just having this provider based um object model you know you can look up and down the widget tree to grab your model and to call your actions but the data is like you never have to worry about updating the data in the wrong place because everything&apos;s like get only and immutable um I think this is a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:41&lt;/strong&gt; · really cool way because you you can run into a lot of issues like oh well I updated this data on this screen but I forgot to update it on this other screen but you know for example am store uh one of the package or applications I built in order to do undo redo uh you don&apos;t want to have to like you know call this um undo stack you know from everywhere&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:03&lt;/strong&gt; · you just have a single place where the data changes and you just you know you can listen to those events and just you know it&apos;s kind of more of a stream based U which I think RX is pretty similar too but um yeah it&apos;s it&apos;s really nice one-way data flow I mean I think Redux got it right for that that&apos;s pretty cool um sounds very interesting I&apos;ll have to definitely check that out too yep and then um what about any uh projects that you&apos;ve been working on uh at least recently projects right now um yes but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:35&lt;/strong&gt; · um very little time and I think I should be able to share more details in a future episode um but how how about you yeah I always have a you know a ton of projects but at least at the moment um flutter editor has been one I&apos;ve been pushing a lot um yes you you talked about the briefly before do you want to uh give us a bit more info about that um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:01&lt;/strong&gt; · so I first of all if you guys are curious you can always um subscribe to my YouTube channel I&apos;ve been doing some live streams of me coding it um but so basically the whole concept of the flutter editor is I&apos;m building a template generator for flutter that is a no code way to prototype and design flutter applications um you know without having&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:21&lt;/strong&gt; · to need the compiler the cool part is if you run it on desktop and you do have the compiler installed it can do a lot of other stuff for you um so it can actually do hot reload it can debug your application it can do all this other stuff but it&apos;s not meant to replace vs code it&apos;s more meant to replace the design side um what&apos;s cool that I got&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:41&lt;/strong&gt; · working uh last night is you can you know for example I you can create themes for your flutter application you can add custom colors you can use the Color Picker and do all that and it actually updates the theme file inside of your flutter application so you you can literally have this open next to vs code um update the theme file and then all of a sudden your app will be running and will show the new changes of the new theme colors um you can change the theme modes and all this kind of stuff you can do linear gradients radio gradients so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:11&lt;/strong&gt; · um the goal is just to really empower the user to do all the stuff that are hard to do programmatically like it&apos;s really hard to go look up like the CSS color codes and all this different stuff it&apos;s a lot easier just to have a Color Picker and just be like use the eye drop tool and be like okay I want it like this color in the app but I want it slightly different shade or opacity um as well as you can edit existing files um it has a cool pwa manifest generator in there so you can like you know customize all your settings for you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:42&lt;/strong&gt; · Progressive web apps and then also one other tool that I built in there which I&apos;m excited about is you can resize your icons so you can upload an icon image and it&apos;ll automatically update your project with the uh the new icons in the correct folder so Mac OS Windows Linux wherever um that one&apos;s pretty cool because it&apos;s sometimes a pain to have to go to update your icon every time um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:06&lt;/strong&gt; · because you have to update it in so many different places and now including the web you know and the the favon and everything it&apos;s just there&apos;s there&apos;s so many things that you have to do and so easy to miss stuff yeah it sounds pretty cool like I&apos;m super excited for uh future updates and your progress on that so um where should people will follow you if you if they want to see what cool things you&apos;re building so you could pretty much follow me on every platform as Ro Davis r o y dvis um that includes Twitter GitHub YouTube and um we&apos;ll have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:38&lt;/strong&gt; · links to all this in the description below uh what about you um yeah mostly the same like Twitter GitHub um medium it&apos;s Norbert um k o Zs i r um Kier um awesome basically um again trying to get&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:55&lt;/strong&gt; · get more um back into posting online stuff and building cool things which I&apos;ve unfortunately been missing out on a bit in the last couple of weeks and months but I&apos;m super excited to um participate um more again yeah absolutely and um if you guys are curious we are both pretty um approachable so um if you have questions&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:19&lt;/strong&gt; · about the podcast or have things that you want us to talk about make sure to follow us on Twitter and give us a shout um by the time we&apos;re posting this we probably should have a Twitter Channel or something for this but um yeah we this is all about the community and what you guys are creating so we are excited about projects that we find and we love to bring up new stuff all the time so um thank you so much for listening if you have feedback um you can also email us um send to us on Twitter and then um and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:51&lt;/strong&gt; · most importantly if you love this please give us a raid on iTunes and a Stitcher wherever you listen to podcast because that really helps this podcast grow thank you so much thank you&lt;/p&gt;
</content:encoded></item><item><title>Flutter State Management and Dart Nullability feat. Simon Lightfoot</title><link>https://rodydavis.com/podcast/creative-engineering/flutter-state-management-and-dart-nullability-feat-simon-lightfoot/</link><guid isPermaLink="true">https://rodydavis.com/podcast/creative-engineering/flutter-state-management-and-dart-nullability-feat-simon-lightfoot/</guid><description>Expert Simon Lightfoot discusses advanced Flutter topics, including state management and Dart nullability.</description><pubDate>Tue, 28 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Flutter State Management and Dart Nullability feat. Simon Lightfoot&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;jgxblf5SW4U&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Simon Lightfoot&lt;/p&gt;
&lt;p&gt;https://twitter.com/devangelslondon&lt;/p&gt;
&lt;p&gt;https://devangels.london/&lt;/p&gt;
&lt;p&gt;Norbert Kozsir - @norbertkozsir&lt;/p&gt;
&lt;p&gt;https://twitter.com/norbertkozsir&lt;/p&gt;
&lt;p&gt;https://github.com/norbert515&lt;/p&gt;
&lt;p&gt;Rody Davis - @rodydavis&lt;/p&gt;
&lt;p&gt;https://twitter.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://github.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://youtube.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://rodydavis.com&lt;/p&gt;
&lt;p&gt;Our podcast player:&lt;/p&gt;
&lt;p&gt;https://rodydavis.github.io/creative_engineering/&lt;/p&gt;
&lt;p&gt;Follow on Twitter:&lt;/p&gt;
&lt;p&gt;https://twitter.com/CreativeEngShow&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · welcome to creative engineering we have another exciting episode today today we have Simon Lightfoot how are you doing Simon hey good thanks and we also have my co-host Norbert how you doing I&apos;m doing great too so Simon how about you tell a little bit about yourself uh we&apos;re I know you&apos;ve been using flutter for quite a bit now actually there&apos;s always the hardest bit of introducing me because i&apos; I&apos;ve just been around for for a long time yeah uh so I guess in general like you know I&apos;ve been programming for like good good part of 20 years I think I&apos;ve lost track and um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · and then 10 years PR Android development and then finally last few years I normally said two but I think it&apos;s three years now time flies but I&apos;ve been doing flutter so we started flutter um Mid Alpha nice and uh yeah so going into the community and um from my perspective we needed it so so I&apos;m also CTO Dev Angels uh Dev Angels London uh small small development agency doing apps and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:57&lt;/strong&gt; · um we had a problem the fact that we had have Android expertise but not much iOS expertise in the company and uh flutter s we at uh was it Google developer agency day Europe what name but um and uh there was an introduction done&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · um I forget his name I&apos;m terrible in names but um there was an introduction done about flutter and I was like all through the slides it&apos;s talking about the JavaScript bridge and how it&apos;s different from the other ones and I&apos;m like oh my God I heard about flut like six months previous and I completely disregarded it I was like this is going to be terrible it&apos;s another crossplatform piece of garbage you know like this is not what I want as an Android developer and then when he&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · start explain what it does and how it works I&apos;m like actually this is what I&apos;ve wanted for years this is really what I want and um and I just immediately tried to out that same day and before I knew it we transition the company up to using flutter um I think literally within the quarter afterwards so very fast transition and all our apps since have been flutter apps wow that&apos;s awesome and I think you&apos;ve been doing a lot of the web stuff as well uh what do you feel like has uh have you noticed a big improvement over the past year or so oh definitely so um I actually um I was uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:11&lt;/strong&gt; · invited to try out flutter web before it was released and give some feedback which I did um and back then it was very well it was separate project right it was a separate um Library it was completely separate from the framework and they&apos;ve integrated that into the framework now performance is way better&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · you know they they they&apos;ve they&apos;ve all those sort of edge cases they&apos;ve kind of sort of wed down to to very few now however when I was I recently required to use it for an actual project of Dev angels and um in doing so I found there&apos;s some other things that weren&apos;t covered so I put some PRS back into the framework so dealing with things like um get rid of that annoy Annoying hash in the RL I it makes sense don&apos;t get me wrong however yeah I don&apos;t like it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:00&lt;/strong&gt; · yeah I I&apos;m kind of both ways on it like I feel like uh it&apos;s nice because most static websites you know single page applications use this but at the same time you know it&apos;s bad for linking I can I can give you exactly one problem that you can actually have okay Apple&apos;s I think I&apos;m going to quot this might be a misquote but if I remember rightly I was implementing Apple signing recently and it strips off the location from the URL wow so you can&apos;t have the hash and the path in as part of the URL when you direct back after o 2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:32&lt;/strong&gt; · great and that which is actually not part of the standard I don&apos;t believe but uh I might be muddling up there with that with another provider I&apos;ll be honest with you it&apos;s been a while but um it&apos;s just that kind of nature of those little things where the hash is meant to be a local thing not a remote things so if you do need to like deep link or link into your app you do need that kind of direct path manipulation rather than um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:54&lt;/strong&gt; · uh what&apos;s it called location right so before like you know you can remove the hash from the framework I was actually getting around it by creating you know when you deploy a Firebase I would have a redirect so take the redirect and then redirect to another page so then externally I would have my redirect link that would then without a hash that would go then to the the page with a hash so about number nums of requests at that point and latency and other things so um so we have a little bit of followup actually uh Dart just announced&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:23&lt;/strong&gt; · null safety I know we&apos;ve been all been waiting for that so what do you guys think about that um so I mean it&apos;s a pretty big deal they&apos;ve been working on this for quite some time I think this is like the feature which pushes The Language by by quite a bit like um a lot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:43&lt;/strong&gt; · of modern languages for example cotland I&apos;m not sure Swift swift yeah they all have uh the sense of non-ability they noce null pointer exceptions aren&apos;t that fun and that Dar is also um implementing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:02&lt;/strong&gt; · this is like a huge step in becoming a asset to flor like a lot of people were complaining okay Flor is cool but D but with this feature and wait wait for what&apos;s that why can&apos;t use C right yes some point people were like will be like okay cool we have Dart that&apos;s a great thing that&apos;s like the language you want to use I I I mean this one of the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:27&lt;/strong&gt; · one of the conversation I had with earli adopters when we were doing a joy con London a few years ago was people were saying that why can&apos;t we use cotland I&apos;m like well Google run the entire stack right essentially I run but you know what I mean like they they essentially own the entire stack meaning that they can make changes to the language that suits flutter and vice versa so we as we&apos;ve seen we saw the um oh I can&apos;t remember his name manificent Bob whatever his name is he did the the language changes for the oh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:57&lt;/strong&gt; · I forget what they&apos;re called collection Expressions I can&apos;t remember I think was that that&apos;s what where you can put your if statements and your for Loops inside of um AR declarations spread operators spread operators as well yeah and uh that is perfectly what we needed for flutter however it was language design change right these are the things that we get with with being able to have the entire stack right and again non-nil ability&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:21&lt;/strong&gt; · right like having this non-nil by default they can Implement that language and and really like do this overhaul because it is essentially a big over whole of the entire framework just to support that um actually I I I submitt a PR on date time um so when you&apos;re passing a this is a bit off topic but when you pass a date time back from I think it was aure back end it formats the date time with like 10 decimal places uh for the micr seconds and um uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:53&lt;/strong&gt; · Dart only accept six right and just because the prision from 80 bit to 64bit or something like that however the difference is it would fail passing the date time not just ignore the lower bit right which is kind of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:08&lt;/strong&gt; · what you want you don&apos;t want it to fail pass and you have to trim the string and then reattach like the time zone and do other weird things just to get to pass the date time is ridiculous so w i put a PR in there and it got proved it got fixed but I had to the key here is I had to implement it in the non-n default as well as the normal uh tree there was two different trees active um I&apos;m not sure if that&apos;s the case now or not but but yeah again like I tried out if anyone doesn&apos;t know there&apos;s um I think it&apos;s nulls safety. dpad dodev yes I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:37&lt;/strong&gt; · think yeah I think it&apos;s just PARTA of the regular Dart pad now right because I think no there&apos;s a separ well I don&apos;t know I know they they advertising a separate okay I&apos;m going to check it now just null safety. pad. cool cool and you can try all the null null safety changes but um I mean as I saying I think um we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:59&lt;/strong&gt; · saying that that that flutter itself is not null safe by default at the moment like the flutter the framework isn&apos;t but dark and its core Sate is that&apos;s what the that&apos;s what the tech preview brings right so you still can&apos;t build your flutter apps as nonn by default at the moment that&apos;s going to come in this hopefully in the Sim future right but there&apos;s still migration work to be done like so I mean null has been used and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · and has been used in programming if I don&apos;t know how many years I&apos;ve used null and had null pointer exceptions trust me you don&apos;t want to even think about null problems in in C and C++ code because then you&apos;re talking about especially you&apos;re talking about like triple D references and somewhere there&apos;s a null pointer somewhere you&apos;re having to deal with this is Nightmare um but uh yeah so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:44&lt;/strong&gt; · so like in flutter like um raise button the Callback so you do on press if you pass null you&apos;ll disable the button right explicit use of null should be frowned upon it&apos;s not it&apos;s perfectly possible right so I think the first version of the flutter framework we probably see is where they just say that can be null right I think it&apos;s the exclamation mark or question mark you put after the type this optional yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:09&lt;/strong&gt; · basically saying this can be null if you want which we&apos;ll probably see but then I feel like there&apos;s a there&apos;s a reason for us to then start porting our code to these things like rather than having give you use I can think of you when you log into an app right you have might have in your app code where you&apos;re logged in and you say oh if I have a user object object in my user stream or something then I&apos;m logged in if I have null I&apos;m not logged in right the the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:35&lt;/strong&gt; · point here is we want to frown away from doing that and instead have a constant value of like user do none so have a static constant inside the your user model called none which is a blank implementation of user which has blank strings for names and so on you can then do a direct comparison because it&apos;s a static con and say is this object identical to this other object it is so it&apos;s not logged in right now so there&apos;s never a case you see null on that stream&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:59&lt;/strong&gt; · mhm and that&apos;s kind of the thing that we need to think about when doing n null at default and trying to you can always start moving towards that but that&apos;s just a good program practice you can already do that right now right that doesn&apos;t involve the new operators new operators have brought about null management allowing you to use null when you want to but to be honest if you can avoid using all together why not and so does this remove the need for using asserts or reduces the amount that we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:23&lt;/strong&gt; · would use them because I feel like the flutter uses that almost always just to check so this is actually this is kind of my Curious point because parameters right when you you don&apos;t speci parameters default to n therefore allain parameters are what default to null i&apos; I&apos;ve not had enough um yeah playing around with with the the the null safety stuff to really look into that in high detail like for me personally when it gets released and when I have to deal with it I&apos;ll do with it then I know I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:50&lt;/strong&gt; · just don&apos;t have the time to really look that far down the rabbit hole right now cuz like do you need to use required at that point oh they have the the new isn&apos;t there there&apos;s a new operator late which solves some issues I mean uh for specifically for constru a Constructor oper like do you need a required because like if you if it&apos;s non-n knowable yeah but the required is just an annotation right yeah but it we were using it right now to make sure that it&apos;s like you were signifying that it has well no it&apos;s just done um at compile time right but at run&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:18&lt;/strong&gt; · time you can still pass null that&apos;s why you have the asserts right yeah you&apos;re right you&apos;re right so so The annotation is just there for as a compiler U flag right as a as a hint to comp to say that this should never be null right but there&apos;s times that&apos;s that can done be done um analyzed during static analysis right not at uh Dynamic runtime but I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:40&lt;/strong&gt; · think with required you can still explicitly pass a null and one complaint right yeah yeah you&apos;re right yeah so that&apos;s where the assert catch can you or can of it&apos;s just when you have a variable that&apos;s null I think I think required only make sure that you actually pass they specify the parameter you have to yeah that&apos;s it sorry so required says you have have to specify the named parameter but it can be null&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:03&lt;/strong&gt; · still right but again like these are the things that you know that the framework team are going to the f team itself are going to have to deal with and say these are the rules that we&apos;re now going to run it by but to be honest asserts aren&apos;t in runtime they&apos;re only debug so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:18&lt;/strong&gt; · you know oh yeah right it&apos;s not going to have any performance impact it shouldn&apos;t do so yeah I was curious actually the stats on the the blog post that was pushed out recently was like 19% uh performance performance increase not speed increase performance increase so that performance can obviously I&apos;m not not trying to subm words here but but performance means generally like lots of things like memory speed CPU usage you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:45&lt;/strong&gt; · know frame rates all sorts of other things right it&apos;s not just specifically how fast your code is run by the CPU right but in truthfulness like if you don&apos;t have to check null or or if there&apos;s no sort of put it this way in in in you end up doing preconditions all the time in in C++ code but you&apos;re just saying oh it can&apos;t be null it can&apos;t be null essentially the same as the way we&apos;re using asserts right now where you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:07&lt;/strong&gt; · you you you&apos;re basically telling it throw an exception if it&apos;s null that&apos;s basically what the um compiler is inserting for us so that if it does find null at some point sorry you do try de reference a null it will throw an exception um and that&apos;s it&apos;s all handled during the processing of the of the D code itself well the compile D code so also yeah I mean when I first came from uh Swift actually I I was very much used to the null safety and optional chaining and all that kind of stuff got into cotlin and then I came to Dart so I&apos;ve&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:37&lt;/strong&gt; · still been using question mark optional chaining for all the stuff except for I just have to explicitly make sure I know which ones I expect to be n which ones that do we I mean do we want to talk about some of the other D features right here CU I mean I mean there is there is that kind of like there is some of those nice things like like having almost a data class where you just have your fields and then you can do a Constructor with this Dot this not this and just have all your you justed here my parameters with this and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:04&lt;/strong&gt; · they&apos;re public final you know you&apos;ve got like your static immutable data class very nice however as soon as you want to say I want this to be private now you can&apos;t do that so right right so now you end up doing that weird thing where you have a f in your in your uh finalizer&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:20&lt;/strong&gt; · after the colon of your Constructor you end up doing the private equals the parameter passed in you have to pull the types and the parameters again like these are little things that feel like these adjusted it would make it that bit nicer to program with but not critical right yeah you&apos;re right um but one of the things I&apos;m going to say this now because you got me in this it just reminding me um fact so you can&apos;t reference Constructors right yeah ex you cannot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:44&lt;/strong&gt; · like there&apos;s no way of saying I want to pass a Constructor as a method which I really feel like like you end up having to do a a a static method right that then calls the structure or something pass it in it ends up just you end up bulking out a coder so a good example of this is what I do all the time especially for my service level as get we&apos;ll get to architecture but um a a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:08&lt;/strong&gt; · static init method right so I have a p i have a private Constructor and a static uh AIT method that returns a future of that class right so the idea here is that I can now initialize stuff asynchronously and then return my instance right I have to use the static&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:26&lt;/strong&gt; · method because you cannot have an asynchronous Constructor in right like it&apos;s just not a thing even you think that that&apos;s a factory method right I mean essentially there&apos;s a difference though between a static method and a factory method a factory method is treated as a Constructor but it&apos;s a method of code right and it&apos;s run before the Constructor so you can get away with doing logic in there right and still have final um final class members right&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:51&lt;/strong&gt; · but then your uh static method is is treated as a method not as a Constructor so so the two things don&apos;t align and you kind of it&apos; be nice to see uh asynchronous Factory methods right so that you can have asynchronous construction then really be helpful to I think to a lot of the way that we do stuff in dark right imagine that you can do a uh construct things in a chain and then return some sort of logic on them and do that all in like a future stream right sort of a pipeline yeah I like the way&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:21&lt;/strong&gt; · Cartland does data classes specifically you can just type Data before and it does uh you know does your equal operators your hash code your you know um your private Getters and all that kind of stuff and I think it&apos;s awesome you can do it now with dart but you have to use code generation or well these are some I think the there&apos;s a language Bard right on the D SDK which sort of goes through something things but there are following up on that one as well as um s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:43&lt;/strong&gt; · classes Sil unions all those kind of good things type A lising which I can&apos;t wait for to be honest because there&apos;s several times I use type A Ling I&apos;m just like why don&apos;t have this oh versus type def what what what do you mean by type well no so so type Def and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:00&lt;/strong&gt; · there&apos;s a good example of this um I&apos;m not I&apos;m I&apos;m goingon to mince his last name Luke petti is that his name I can&apos;t remember he&apos;s how he pronounced his last name he did this on his Twitter feed recently and it was um having a mixing so you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:16&lt;/strong&gt; · like Define a new class with different mixins and you&apos;re defining you can essentially Define an aliis with his different traits right but then you try and do that with just with a normal class you use the equals operator in between it doesn&apos;t work right there&apos;s just some inconsistencies in the way it works right and typ lising do does that thing where you can say I give an example of this actually had this recently uh well one of the things we&apos;re missing right which I really like this is terrible what I like from java um was uh that that enum class enum&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:48&lt;/strong&gt; · was a class right it was a it&apos;s a base type it&apos;s also a class I mean I could add private member variables to an En them right so I have things very common case for me in terms of of rment is that you have a set of defined States or things that you return to and from a server for instance now during the serialization Der serialization process you&apos;re treating that as a as a string right but then you want to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:13&lt;/strong&gt; · when you receive that string put back into your typed object so you can look at the type look at other other related variables to that type so they might be your private for example you might get back a state that says pending right lowercase simple string right but then you want to say the user currently waiting for Server feedback right so you want to put that string or or a reference to your localizations for that string in with that IM in that enum so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:39&lt;/strong&gt; · they&apos;re tightly coupled so that when you display it to the user and you get back that enum you can detect oh we&apos;re in the pending State and then I want to display string display the one from the enum or look up the localization from from the one that the enum&apos;s given me so I&apos;m kind of tightly coupling these things together this is essentially like a union right uh s classes as well can be used for that so speaking of cotland data classes I I think it&apos;d be cool to see something in Dart where you know we we have kind of this more um flexibility&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:09&lt;/strong&gt; · but a lot of the stuff we&apos;re having to do by boiler plate and code generation kind of be cool if like kind of Dart took it on itself because like you said enum with a class another cool thing about that is you can Rite actually extension methods onto a class you can&apos;t do that with an enom right now um yeah because I&apos;ve I&apos;ve wanted to do stuff like that as well well I mean that&apos;s where the so that&apos;s where type comes in right so in tying you can say U give a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:32&lt;/strong&gt; · good example is if you want to bind some things tightly couple some things together you can create a new class which is a string right so good example you can&apos;t put an extension on a string right right now but if you create a new type called uh server state right which is a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:50&lt;/strong&gt; · string and then you could then extend it with other things during run time there&apos;s a lots of ways to C things together but also it&apos;s about not miss using extensions cuz that&apos;s one of the other things right like it&apos;s too easy to misuse extensions and I&apos;ve seen it already set in several bits of code I&apos;ve seen like just throwing extensions around like all over the place and then like like where these things could have been methods these things are not do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:14&lt;/strong&gt; · not you&apos;re using extension as a hack rather than actually yeah architecting your software correct well not correctly is the wrong word but you know like like like put this way you don&apos;t look at a class and going oh this is the class and then figuring out oh run time no it has these other properties that are extension methods I have no idea how these where these things were coming from right yeah I tightly coup them together they should be I feel like that&apos;s where mixing coming right so I feel like there&apos;s a use case for all these different ways of doing things but the right thing for the right job and extensions are there for me personally&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:46&lt;/strong&gt; · is to actually extend things out of your control right gotcha so so for instance if you got a package coming in from externally or you&apos;ve got something in the framework that you&apos;re trying to extend right for instance one thing I did recently which actually is part of architecture is just to um extend put extension on stateful widget and stateless widget that has a simple thing like um database repository or database&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:11&lt;/strong&gt; · service that is a a getter right that goes and uses provider of context or whatever to go fetch it right so instead of having the pro now here&apos;s the key so my I&apos;m still using the provider package as essentially an inherited widget but I&apos;m using it in one file&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:28&lt;/strong&gt; · I&apos;m not sprucing that that provider input all over my application my application has no clue what provider is because it&apos;s only using the one place I&apos;m using it where I have a widget that provides things down the tree and then my my extension that goes and uses provider to fetch them from the current context yeah also I um I&apos;m not sure&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:48&lt;/strong&gt; · where I&apos;m doing something wrong but um when using extensions I cannot Auto Import them like I have an extension on something and I know the extension the extension is defined somewhere in my project but I have to manual import a file they did add that recently I believe however it only works on named extensions oh got you because that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:09&lt;/strong&gt; · that was always a big thing because I knew I had that extension but I had to manually go in and search for the file where it was in yeah but to be honest I mean here&apos;s a good the good example of that is using export right you should be exporting those extensions right so that your inut when you import like uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:26&lt;/strong&gt; · certain certain something from your app you want to bring the extensions along with it well then export them from that import does that kind of make sense so that way your the extensions come along with anything that uses that that that&apos;s that type of system sometimes you have stuff on external libraries for example if you want to have an extension on datetime for example you cannot export&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:46&lt;/strong&gt; · datetime with your thing one one thing so but what you can do what you can do is create one file which exports date time and exports your your no seriously you can export date time and Export your EXT exension but you have to keep in mind to import that file instead of yeah but when you do your Auto Import it&apos;ll give you the options of both yes that&apos;s true right and then you get to choose the other one right but I mean yeah sure nothing&apos;s perfect right I&apos;m not going to go there I mean these are all there&apos;s so many improvements that can be made but this is all about I mean it all takes time and what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:16&lt;/strong&gt; · is worth the changes and these aren&apos;t worth any changes right this is all about using the language effectively right this should be a new book effective do yeah so I mean this this kind of brings to what we wanted to talk about today which is you know like State Management for flutter and um specifically we want to talk about you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:35&lt;/strong&gt; · know like what do the why do we want to choose a state management solution and how can it help us because I feel like you know especially you know feels like you know I first started with flutter was like beta 2 it&apos;s like you know we had all these things thrown around it&apos;s like oh scope model and streams and block and it&apos;s like oh well block is crazy big and there all this boiler plate and there&apos;s Redux and you know you have to do all this and they all have their tradeoffs and it&apos;s like all of a sudden we went from like crazy uh just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:02&lt;/strong&gt; · you write a bunch of code to the all the packages are writing all the code for you but like you don&apos;t you can&apos;t quite predict what&apos;s going on some you can so yeah just a a lot of different things going on right now I mean to be honest someone asked me the day what one do I think is better what what um what state management solution would I recommend and it&apos;s like chose your poison right cuz the the term is actually incorrect right I mean we&apos;ve talked about this for a while State Management is because you in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:30&lt;/strong&gt; · in in flutter specifically talking about UI state right and managing your state of of your application and its UI because mainly the UI is reactive and that&apos;s what builds and then that sort of affects your logic in some fashion but what you want to do is I found I mean I have to remember early on in the very flat early days even myself was guilty of this of just inlining every function as part of my you know my uh widget widget tree and just yeah mish mash of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:57&lt;/strong&gt; · of code and it&apos;s more about sort of separation of concerns right so uh I think separation of concerns is actually one of the solid principles of design from Uncle Bob but um it&apos;s one I go to as well dry and keep it simple and all those sort of standard things but there just just sort of things to mem to kind&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:16&lt;/strong&gt; · of just to keep you sort of directed at how you should be doing stuff right so it&apos;s when you do anything OB Ted it&apos;s always about separating off naming things appropriately structuring things to communicate with so that when you come in and modify them you&apos;re not having to trace down and refactor and just rewrite huge swaths of your code you can just keep it in one location yeah that&apos;s really sort of like that sort of En class en sort of encompassing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:40&lt;/strong&gt; · your logic in one place or your your your your code in one place I think that&apos;s really what is which is architecture right that&apos;s how you architect your the classes of your application to interact with each other to produce your desired result of your app right so to produ produce that business logic so when we talk about these patterns like orig originally the patterns that we were seeing were talking about inherited widget right which is just reaching up to get state from other widgets right because the widgets would be driving the logic right yeah all of here anytime you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:12&lt;/strong&gt; · interact from the widget tree you&apos;re always using inherited widget doesn&apos;t matter if you&apos;re using provider scope model any of these they&apos;re all based on inherited model I this this is actually okay okay most of them okay yeah however okay mobx right now things but um the the point here is why does inherit a widget exist in the first place right what why is it trying to solve do you know Ro do you know know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:38&lt;/strong&gt; · it&apos;s a it&apos;s just a tree data structure you want to have your logic you know where you can grab from one instance of a node to another side of like it&apos;s just a just a shorten traversal yeah right so in terms of it&apos;s a Time Saver and a performance game that&apos;s that&apos;s it&apos;s it&apos;s so so basically what happens is you you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:58&lt;/strong&gt; · it will go up the the first time right your your access they they have changed this logic recently and it does work slightly differ so I&apos;m not going to get this perfectly right correct but the initial the idea is like whenever you need to look up the tree right you use your current context right your build context and you&apos;ve got your they&apos;ve renamed them so they&apos;re now all find by fine widget by this and F state by that and find inherited widget by this and um depends on that widget so I won&apos;t go into this a whole list of them however they&apos;re all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:30&lt;/strong&gt; · about tree traversal right so they go up the tree now if you look at those functions so if you go into um the flutter framework and look at the element class element is what implements build context right so every build context you see is actually your element for your widget right and what you&apos;ll do is it literally traverses up to its parent and says is parent um this type&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:54&lt;/strong&gt; · or let&apos;s say you&apos;re looking at widget so it looks at is parent element widgets this type no then go up to next parent and next parent and next parent so you go this ancestor path right now the key is going up the tree is always less expensive than going down the tree because you do a non-exhaustive search up the tree you&apos;re going to your direct descent ancestor right if you go down&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:14&lt;/strong&gt; · the tree then you have a depth pass or breath pass search in terms of going down to every Edge Leaf node of the tree yeah so so up as logarithmic so or even less actually yeah but the key being here is if you go up the tree right you&apos;re still looping over you&apos;re still doing those if checks so if you every single time you needed to get that parent state or class you have to do that traversal that&apos;s a waste of time right waste of waste of performance especially during build or other things that you need to get things done in your frame limit right so then you kind of go&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:45&lt;/strong&gt; · down and go well okay how do I solve this well if you&apos;re trying to solve yourself you can solve this yourself and that is oh I will create a hashmap right and I will will key my type right and then I will store the value as being whatever I get from up the tree now the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:00&lt;/strong&gt; · first time I get it I store in that hashmap and now each time I want to get a different type of store in that hash map that&apos;s literally how most of these sort of state things work right however they do the the next thing which is that hash map is red further up the tree so if if that type changes it updates the hashmap and notifies the children to rebuild so really truthfully there&apos;s two things to this is one is to Performance are going up the tree and also to notify the children that they need to be rebuilt further down the tree without building the entire chain of widgets&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:30&lt;/strong&gt; · right so it&apos;s a performance win right and it&apos;s just a different way of doing it&apos;s the reactive model so so that&apos;s inheritor widget right scope model is just so so inheritor widget was was inherently untyped safe should we say there&apos;s a and there&apos;s some few sort of uh problems around using and it wasn&apos;t it&apos;s not easy to get your head around it even now right so so scope model came out scope model by um the barosaurus&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:56&lt;/strong&gt; · himself Bri yeah I I love calling him that it&apos;s just every time I see him in that nice beard of his it&apos;s like how how did he do that that&apos;s amazing um the up keep on that thing BL me anyway the scope model uh it&apos;s sort of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:14&lt;/strong&gt; · like just giving you that nice interface on top and then giving you sort of like this idea of a model that you can then notify when it field changes and it updates the wi is essentially the same thing and then uh there&apos;s a few I&apos;m trying to remember there were a few other things that came out in fact I think the firsted was out sorry I said&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:31&lt;/strong&gt; · Redux came out right right around yeah Redux now I&apos;m I&apos;m I really love the notion of a unidirectional data flow of a mutable objects I mean that is like like you have a direct idea of the flow of your application and one of the killers of most apps in fact I know back in the day when oh my goodness like 10 years ago when I was first doing Android apps was that State uh like having mutable State and and changing the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:01&lt;/strong&gt; · properties was leads to literally every single bug you can imagine and by just changing things to having immutable models solved so many issues because it meant you didn&apos;t have the possibility of um not knowing that something had changed that you have a direct notion that the truthfully inside the um it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:21&lt;/strong&gt; · about Java engine it happens in D most of the object oriented systems is each object has your ID it&apos;s it&apos;s it&apos;s uh literally it&apos;s Unique identify in the in in the system when it&apos;s been allocated and that by default is the hash code right so that&apos;s so if you compare two hash codes of two objects that don&apos;t have an equals method you&apos;re essentially saying are these two things identical right by reference right by well uh yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:48&lt;/strong&gt; · I mean as an object it&apos;s by it&apos;s passed by reference but still you talking about the value the they have the same reference yeah yeah because they both exist in the same location yeah yeah yeah so so the anyway anyway I digress but the the point here is that uh scope model sort of even off the playing field&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:06&lt;/strong&gt; · Redux comes in and we get this unal data flow we get the Redux behaviors which work well with react that kind of brings another lot of people into flutter in terms of react developers and so on like they they used to using Redux and a lot of people have been using Redux in their apps then you still talking about RX right using RX with Redux and that kind of sort of brings in a new way of people and a new way of do things because there&apos;s several things that are difficult to do not impossible but difficult to do with streams in dark then RX came in using it with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:37&lt;/strong&gt; · observables and then finally I believe as of I&apos;m not going to quote the right version of I think 023 of of rx. it now uses streams directly so you can now use nonobservable base patterns so now stream because of extension methods it now has all the standard sort of flat map on a stream mhm so you can just flat map the direct data typee and not have to worry about like wrapping every one of your data types with an observable patent right and having the next done and all that kind of jazz the anyway&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:07&lt;/strong&gt; · back back to back to Redux yeah yeah so so we had Redux and then what was the other history we had Redux umex came out sorry about M was after provider right so provider I think is the next big leap I mean for sure sorry it was block for sure block no sorry yeah no you&apos;re right block and relux are around the same I think they came around the same time because I remember lots of conversations were about which one should I choose right yeah but again block I personally&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:34&lt;/strong&gt; · have never followed block to the letter of the specification right yeah same in terms of I don&apos;t use syns a lot of the time like to send data to the blocks right I don&apos;t let it reveal a sync and then push stff into a stream great for when you want to link blocks together and have stuff streaming through the pipeline Right Where You reveal a sync and you can attach things together right but when you&apos;re talking about UI actions I&apos;ve never gone that route of yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:59&lt;/strong&gt; · depends on what I&apos;m doing right but I never really go that route of having a a value or an event or something that I&apos;m passing over a stream to the to the block and then having the block churn that change it state I&apos;m listening to the state on the front end and then that returns back the uh the new date or the update state with which contains whatever so so yeah so one thing which&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:19&lt;/strong&gt; · is also uh interesting to talk about in this context is um apps have a very specific way of handling data and that&apos;s a reason why we need those things like streams Redux um and that&apos;s because apps are very reactive like every uh possible configuration all the data um is based off of events a user Taps a button something comes from a server some sort of response something is loading so most&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:47&lt;/strong&gt; · parts of an app are asynchronous and very data driven if they wouldn&apos;t be the case like for example in servers where you have one request which is coming in it is being process it&apos;s being piped through a lot of systems you wouldn&apos;t need all this uh reactive uh Behavior&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:04&lt;/strong&gt; · but for apps um everything is interconnected and reactive and that&apos;s also mainly the reason like um why we actually need these kinds of streams why we need some sort of pattern which observes data instead of um setting it I&apos;m I&apos;m going to have to disagree just slightly not that that&apos;s not what it is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:23&lt;/strong&gt; · right now for flut but more about apps in general right so we&apos;re talking about about flut apps because the reactive UI it makes sense to have no no I&apos;m talking about apps in general like I&apos;ve been doing Android before too and I know having mutable data which you have to keep track of yourself like when is it changing I have to know you but one trying to say is you still have a mutable data you but but you don&apos;t have to have streams necessarily like like simple patterns of like a holder pattern where you&apos;re hold you have one object that is constant that holds on to a value as a reference and then that has a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:54&lt;/strong&gt; · notification when the value changes these are other patterns that can be used used yes but those are I dig I digress sorry definitely Yeah so basically the idea is instead of having to manually keep track of when do I have to rebuild when do I have to show something new to user we lay off the responsibility of uh knowing when we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:13&lt;/strong&gt; · have to rebuild to the actual part which is causing the rebuild I think there&apos;s also an interesting point that&apos;s was going to take me back but when doing Android a long time ago when RX Java came out on Android mhm and we we started going with uh Java 7 lambdas right it suddenly became a pattern that you could then use uh RX effectively&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:37&lt;/strong&gt; · inside of inside of your Android app because you didn&apos;t have to do these Anonymous uh class instances all over the place you just use Lambda expressions and um the power really comes from set up a pipeline right so so essentially set up your stream notes having your flat maps and your your different manipulations of the data it flows through the the pipeline and having essentially you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:59&lt;/strong&gt; · could express the entire back end of your app as an interconnection of all of the pipes of the stream right I mean it would be nuts right and trust me I&apos;ve seen people do it and try it and all sorts right but and you go through a phase when you&apos;re trying RX you&apos;re trying to do these kind of nice little cool things right yes I feel like there&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:16&lt;/strong&gt; · there&apos;s like using anything effectively is don&apos;t like like they say taking anything up too much is bad thing right like having too much coffee is going to be a bad thing right the point here is like doing any one of these things over the top is always a bad thing so I saw someone talking about this was a while ago when RX actually when RX first came out in in flut they were trying to make all of their widgets stateless yeah having like all the state&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:38&lt;/strong&gt; · pushed over to into the Redux data stores and having all this kind of like it was like in general the the principle sounds nice right but then you&apos;re you end up doing things like there might be state that only has to exist for that widget is is part of the UI State at that point it depends and Depends the play should be located as inside the state for widget if that kind of makes sense it&apos;s not something that you should be putting it outside the I mean where else is it going to you could back a class to every widget right so every widget can have a backing class with its logic but then it&apos;s just a base&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:10&lt;/strong&gt; · class with functions in it and state right with variables but you&apos;re just moving the problem right it&apos;s nice for organization don&apos;t get me wrong but that&apos;s again that&apos;s architecture that&apos;s not really the solution anyway like bottom navigation I&apos;ve seen people create a block just for the bottom navigation bar and I&apos;m like you know it&apos;s like a self responsible widget there it&apos;s like only UI navigation if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:30&lt;/strong&gt; · you want to change the index do it with the Constructor and a did update widget you know it&apos;s so much EAS that way I think there&apos;s yeah there&apos;s a point here where you&apos;re working against the Frameworks I mean the framework comes with tools so you don&apos;t have to use any of these patterns right essentially right the Bas things are all there like was just said with inherited widget but I mean go back so so we I mean I have these different patters right so we said Redux provider is one we haven&apos;t spoken about so provider is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:55&lt;/strong&gt; · the to me the the facto standard now in terms of instead of using heritor widget I go for provider right yes mainly because it does all that hard work for me and it&apos;s been heavily tested it&apos;s got L stuff and I don&apos;t have to deal with some of the manua around setting up an inherit widget so you have to set up another class since you got scope class and some other things and also the fact that provider is really it&apos;s scoped model on steroids in terms&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:22&lt;/strong&gt; · of that you have all those other providers that take things so you can have a change notified provider so the change Notifier is it listens to that so that when the when the change Notifier is notified it will rebuild the widget so again all these other uh patterns of yeah that all these other libraries your the way you structure your cocon all depend on that sort provider system right well there&apos;s also another type too which I I use this data or State Management solution for up until probably about six months ago and that was using um more or sqlite on the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:55&lt;/strong&gt; · back end having a unidirectional flow where all my I&apos;m reading the data directly on the widgets so that you have the stream listing on individual stuff and then just using like a block or something to update the database um in a single layer but because that that management was also pretty popular but um yeah I I just curious your thoughts on that too because like you know that&apos;s it also you know obviously depends on the data but you know if you have a structured data kind of approach it uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:23&lt;/strong&gt; · it you know kind of can be nice to have like that unidirectional flow that way too oh for sure I mean it really depends on what you&apos;re doing because I to be honest I would keep it&apos;s about laying your application architecture then right for me so I might have my front end UI deal with streams of my own data types in my application I&apos;m never going to pass a document snapshot right from fir&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:46&lt;/strong&gt; · store to my front end of my application right because now suddenly my entire application is now dependent on Fir store right so if I need to refactor F out I want to switch to my own API I switch I want to put a caching layer in with SQ light for instance then I I just kicked myself right you know it&apos;s not it&apos;s not going to be an easy change to make right so you want to control each layer and keep each layer of your application independent so that kind of goes into&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:17&lt;/strong&gt; · like I mean for for what I was doing back in it&apos;s quite funny what I was doing back in the Android days was using uh what we call repository service model um I&apos;m trying to remember there&apos;s some other better names for this but this is the way I remember it right and it&apos;s that notion of having uh units of responsibility called repositories right they they are responsible for some unique fact think of it like a block if you will but some some uh either some data type or some manipulation of of something within your app so let&apos;s say you had um let&apos;s say you were a music&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:48&lt;/strong&gt; · plan you had albums right so you&apos;d have an albums repository right now all your front end whenever you need an album you need get like an album cover or ID whatever it might be you&apos;re going to go to the repository to to deal with that um data type right or or manipulate&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:04&lt;/strong&gt; · albums in some fashion right but then the albums might have to go off to fir store the albums repository might have to go to fir store to actually go retrieve it from the database or go then go off to Google Cloud Storage to retrieve the the cover URL so I can pass back in memory one and cash it in memory and whatever I want to do but that&apos;s all business Logic on the back end of your application it never touches the UI right the IDE on the UI I do I get an album model and it has all the fields I need to display my on my screen right so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:31&lt;/strong&gt; · essentially almost think of it like a view model in that sense right but that&apos;s the pattern I feel like is is just the most flexible rather than having suddenly like you obviously essentially it&apos;s a bit abstract but you can also then go one level deep and have something between repository and your UI and have controllers right that sort of notion of I have a top level controller&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:52&lt;/strong&gt; · for my application which kind of wires some stuff up or essentially block or so it&apos;s another model I&apos;ve done where you have blocks repositories and services and the blocks just provide that bit bit business logic in between your widget layer do the reactivity part and then go off to the to the repository layer of the surface layer to do things right but the idea is that you have a repository layer which is like as said like albums and then above that if you had firea that would be a service layer right your choice as to whether if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:21&lt;/strong&gt; · you&apos;re doing this model that I use is if you want the service layer to return back data models from your class or if you want the repos or if you want the repository layer to also have access to fire store in that fashion right so it gets the document snapshots and then you deal with the mapping from from essentially from U serialized data type or transmission data type to your local model data type if that&apos;s going to happen at the repository layer or the service layer that depends on your application um I personally try and keep&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:52&lt;/strong&gt; · two sets of models right so you have your model that you&apos;re using uh within your data within fir store within your database that might be IDs for other objects right but then when it comes to your object on the front end you want to link those two objects together so you might do two fetches or or maybe have it in cash so you want to do two SQL queries and bind those things together and then you&apos;re passing off to the front end so the front end has what it needs uh for to display that data for what it needs to manipulate that data and that&apos;s what your application does right so so really the tradeoff and the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:22&lt;/strong&gt; · other trade-off here is that your uh repository and service layer must not you need to keep it completely flutter free right so you never have providers in those you don&apos;t deal with any of the if you&apos;re dealing with um the flutter um any of the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:40&lt;/strong&gt; · flutter sort of framework in your back end then I feel like that&apos;s probably not where it&apos;s supposed to be because flutter is like your UI your front end right right right I try and keep them off however saying that there is one dependency which you can&apos;t do at the moment that is change Notifier or value listenable those things in flutter foundation so you know you&apos;re not going to import just those so do that kind of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:02&lt;/strong&gt; · import show you know show value listenable you&apos;re just importing that one thing you&apos;re not going to be right chanced into using those classes from flutter which you shouldn&apos;t be using like a good example of this right this is just silly but it&apos;s a good example of when these these practices are important color color as a class is a UI do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:23&lt;/strong&gt; · specific attribute right if you want to now take this model object or this other piece of code and put it on your back end and use it in your server side or do some other stuff with this backend piece of business logic or move stuff around suddenly you&apos;ve got dependency on flut right so instead what you should be doing is storing the integer representation right which is your which is actually what color is in in in computer terms color object just literally hides an INT value inside it right with methods right there&apos;s a good&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:51&lt;/strong&gt; · example of an alias type right if you could do an alas type and color would be a good example of doing it right you can have color B and aist of integer with the with the methods on top so you&apos;re exing essentially extending it&apos;s not really what it&apos;s for but digress anyway I know what you mean yeah anyway anyway&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:07&lt;/strong&gt; · back to the point here is that that a good use of that is putting color in your front end models but not in your backend models right so what you&apos;re sending to and from the server is either a string or an integer or some other representation of your color and then you transition then you convert between your your your meta type or or or&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:24&lt;/strong&gt; · wherever you&apos;re trans transmitting serializing back and forth from your services into your model that you use for the front end and then you convert it to a color then right this gives you that layer of control and complexity that you can get around right and then you can prepare your front end models however you want in your apost layer any I digress sorry yeah I think by the way uh and just a quick note on the models and repositories and Services I think uh arcs command was a state management solution that came out that was um kind of all built around that specifically on top of streams yeah yeah so Thomas is uh RX&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:56&lt;/strong&gt; · command with uh to use RX D and also use along with get it and kind of that kind of stuff so package he came out with RX VMS as well which is the RX command as well um personally I&apos;ve not i&apos; I&apos;ve I&apos;ve actually worked with uh on a few projects with Thomas actually and uh we&apos;ve used those patterns personally I feel there&apos;s use for these things where sort of for me Rx VMS is more like a node&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:22&lt;/strong&gt; · right so you so it&apos;s it&apos;s kind of a node from block is it like a front INF for Block which gives you a stream for when it&apos;s loading for when State changes and you kind of like you have these streams now it means that you can connect multiple blocks multiple of these RS vmx blocks almost together so you can have loading go from one as an input to another so one one&apos;s finished another one starts and you can have RX commands being sent and transmitted between these things so any any um it&apos;s it&apos;s a nice system but I find it like for me personally I find most of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:52&lt;/strong&gt; · these things overly complicated right like keep it simple right right like for me block is overly complicated for most of my most of my use cases I don&apos;t want to have to go I feel like I&apos;m put an extra boiler plate in the same as Redux I end up putting way more boiler plate into my applications for for for not&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:09&lt;/strong&gt; · that much gain right I feel like don&apos;t get me wrong use a pattern and use it throughout your application but it doesn&apos;t mean you can&apos;t use multiple things together like you can use provider with Redux right like the two things are not like don&apos;t have to be separated you don&apos;t have to you can&apos;t I mean in the end architecture all it is is to make code for you easier if you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:33&lt;/strong&gt; · write good code which you can edit fast which you um can modify um understand and build on top of which doesn&apos;t use any kind of architecture but just mutable variables that&apos;s fine if it works but most often not it doesn&apos;t work people get confus buckon and just keeping as much as simple um is in my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:57&lt;/strong&gt; · opinion key like if you have a stateful widget which has some logic which is very encapsulated which doesn&apos;t have to be connected to our blogs to streams put it in a widget in a state for widget use that state you don&apos;t have to use streams and all that stuff but once it gets interconnected with your other system it fetches data from that uh it combines it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:19&lt;/strong&gt; · with some data from there suddenly this simple widget which should have just taken one second to skim over understand requires a lot of uh mental capacity to understand you have to go through your project and look there okay make sure you don&apos;t break something there um so in regards to boiler plate&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:39&lt;/strong&gt; · if the boiler plate helps you to reduce the mental strain understanding this component it&apos;s great but if you have something simple which boil plate code adds on top two and like it makes you feel uncomfortable looking at the code I know this feeling if you have this code code which you really don&apos;t want to touch bad smell code right this Cod got bad smell to it sometimes I mean sometimes it could be good architectur code but just over engineered like it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:06&lt;/strong&gt; · solid good written code but it&apos;s like this simple feature which you just want to edit and you have to look at these two different places the other thing to mention here is is testing right like like these architectures and patterns all afford you to be a to test them&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:23&lt;/strong&gt; · right so so the case in point there is if you need to be able to test that piece of logic make sure you have it doesn&apos;t matter if that you&apos;re that you&apos;re not using block right you just test it a different way but my point here is just just make sure that that testing is part of that whole whatever architect you come up with you know you need to make sure that you&apos;re you&apos;re covering all those edge cases that you that you might not possibly think of at runtime yeah yeah especially and also I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:48&lt;/strong&gt; · think um not just testing but be able to debug like if uh like a user has an issue I think that&apos;s a really cool feature of blocking in Redux for example you can actually replay the state of the error of what they had you can see all the events leading up to that and everything at the whatever was happening at the time in fact I did something similar in um it wasn&apos;t Redux but I did something similar and that is um a nice tool that you can do is to keep a log of the last X transactions if you will or&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:17&lt;/strong&gt; · whatever uh events that occurred within your application and these can just be any event you like right just just imagine there things that have occurred like oh the the the app when fetched like the list of albums right from from a repository now that&apos;s an event you log that event so you have a local log within your application and it&apos;s a series of log entry items right and then what happens when the crash occurs you can now collect that up serialize it into a um Json array or into into into a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:44&lt;/strong&gt; · text block and that gets dumped over to your your exception capture crash capture error reporting system and that gives you that kind of event log of what happened this is not just Redux is what I&apos;m trying to get right like like yeah you know and and the same goes for yeah there&apos;s lots of these things right like like like nothing you&apos;re you&apos;re not limited what why one of the reasons why I really like flutter and D is the limitations are gone yes you have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:10&lt;/strong&gt; · no idea I don&apos;t think any anyone that hasn&apos;t used Android as long as I have for that 10y year period and knows it&apos;s kind of funny someone asked me like why I can&apos;t remember it was it was like why something happens in Android the way it does I&apos;m like oh that&apos;s because in version one it did this you know and it&apos;s still there to this day and it&apos;s like there&apos;s this so it&apos;s almost archaic&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:30&lt;/strong&gt; · right in terms of again it&apos;s an operat system you has to be it&apos;s one of the reasons why they didn&apos;t put their move to using the support library right and and and yeah Android X and all this is because they don&apos;t have to put the the widgets in the framework anymore where they would you wouldn&apos;t get those functions and it was such a pain to having to backwards compatibility and having to say oh if you don&apos;t have this function you should use this other function I&apos;ll I&apos;ll I&apos;ll put my own um polyfill in its place to to perform&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:56&lt;/strong&gt; · those necessary features to do something that the platform now contains as pointless I might as well just do it in my own application at that point right yeah so that&apos;s where there s compact libraries came from and all this kind of support stuff but the entire system is still limited like like just trying to deal with like resources and resource&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:12&lt;/strong&gt; · qualifiers and The Ordering of resource qualifiers and how it&apos;s going to select the right resource on Android is just just mindboggling a lot of the time right and you end up I remember a lot of the time in some big applications I worked on I ended up doing type a uh resource a lising which is when you basically add a fake XML tag which is a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:29&lt;/strong&gt; · resource tag which basically just directs to another resource so you can get around the limitations of the of the resource selection so on the specific use case with a specific language in this local and then I want you to use this very specific layout and not the default one or something right like crazy stuff we in when sun and and doing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:49&lt;/strong&gt; · flter I&apos;m like oh wow just assets I can put them in any foldo I want I can struct them however I want I&apos;m free and it&apos;s like like this the same goes with code like people it&apos;s kind of interesting because it&apos;s kind of two fundamental patterns right people come to and um people come to flutter and D like localization how do I do it what what what patter Master use what what however you want yeah whereas the answer is is as long as you get string load it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:14&lt;/strong&gt; · from a Json file on your assets You Know download it from a server you have the freedom to perform those things however you want right and to do them however you want can parttime runtime you name it you can do it the freedom is there whereas on Android it&apos;s like no you must do it with the the XML language options you must do it like this and if you don&apos;t want to do it like that you have to implement the entire framework yourself and override a load of w widgets and and you know views and how you do all this it&apos;s like yeah sorry digress but you get my point it&apos;s that freedom but even like having the freedom&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:44&lt;/strong&gt; · is great but sometimes having too much Freedom could be a curse too like you know when there&apos;s so many options like especially if you&apos;re new you&apos;re coming there and everybody&apos;s just telling you okay do whatever works for you and you&apos;re like I have no idea what works for me um there&apos;s a name for that information overload or that kind of like it&apos;s kind of like there&apos;s so many options I&apos;m I don&apos;t know where to turn&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:08&lt;/strong&gt; · MH yes right that is the problem right that we have in fluttering D A lot of the time is like like we talk about like people this really where that question comes from what what state management solution should I use is like there&apos;s so many to choose from there&apos;s so much stuff to do not understanding it right as a as a as a princip bers and AR like the fact that it&apos;s more or less architecture right it&apos;s not about the term State manager is really the wrong name for it&apos;s architecture a lot of the time right for us it&apos;s more about the state management is just a tiny little bit of uh traversing this the tree the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:39&lt;/strong&gt; · widget tree to get to the to your your widget state righten state management came from is that kind of manipulation of your UI state but truthfully like it&apos;s like block isn&apos;t that right block isn&apos;t State man block block isn&apos;t architecture pattern right like like it&apos;s a way dealing with UI right same goes for providers is inherit widget&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:00&lt;/strong&gt; · it&apos;s like they&apos;re kind of just traversing the Tre tree providing some of that sort of extra stuff that we kind of want but anyway I digress right so sorry well also you know uh with that too people you know have all these things when they come to flutter and they they see these options but you know one thing that I&apos;ve been doing recently is I I just ask them straight up like what&apos;s the data doing like how is the data flowing because nine times out of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:23&lt;/strong&gt; · 10 the data decides the State Management solution if you&apos;re building a graphics application like I did with a widget Studio I&apos;m not going to be using SQL light and all this kind of stuff to store all these individual things most of it&apos;s going to be in memory most of it&apos;s going to be some sort of serialization and uh I mean I use value&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:40&lt;/strong&gt; · notifiers to update individual things but then if I&apos;m building you know a business application I&apos;m going to be using some sort of sqlite or you know using something like that so one thing I just thought about inherit we cannot forget inherited model which was added later on in the game y um also used I think by scope model in the end for its model stuff and there&apos;s a few other places it&apos;s used um I&apos;m not sure if Remy&apos;s using it but I know the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:04&lt;/strong&gt; · new select does essentially the same thing where you can essentially uh look at when a specific field or value has changed rather than have to rebuild on whenever an object changes so you get that kind of fine finer level of of control um but yeah but most of this is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:19&lt;/strong&gt; · it&apos;s just about the question I always ask people like trying to ask they ask a question like I&apos;m trying to think of how do I position a widget like how can I position a widget a specific XY coordinate you&apos;re like but what are you trying to do and they kind no I&apos;m trying to position a widget no no no no no no that&apos;s your solution but what is your problem you&apos;re trying to solve and when they explain the problem that they&apos;re trying to solve it&apos;s like no no no no do it like this this is the reactive way of doing you should put this widget in there you should put a line in there you can animate your line and that will give you your best smoothest way of doing it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:46&lt;/strong&gt; · and no matter what size of the device is it&apos;s going to like Traverse the entire screen you&apos;re no longer dealing with pixel coordinates right like yeah the these just it&apos;s just ways of thinking about this the key that one of the reasons why flutter is just to me a good thing right but it&apos;s also it&apos;s a good and bad thing right fact of it&apos;s so different that it solves a lot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:06&lt;/strong&gt; · of the problems that we&apos;ve been having so it&apos;s like if I was to do if I was to be on the team that wrote Android again and we had a chance like just that&apos;s it that&apos;s it we&apos;re just going to kill Android right now and we&apos;re going to rewrite Android no no the point is like like Android 10 or whatever version right is going to be a ground up rewrite of the entire Android system how we&apos;re going to do it some of the principles at least of flutter are the ones I&apos;m going to be choosing for for doing some of this stuff because of those of the way&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:31&lt;/strong&gt; · it really brings the the the developer experience of making apps forward right where it needs to be and that kind of sort of goes back to that point of um now I&apos;ve lost my train of thought well I would just say real quick you know it&apos;s nice that you know people all people that are kind of on the fence of flutter don&apos;t realize that in 3 years flutter can still be whatever it wants because it&apos;s not tied to Android or iOS&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:58&lt;/strong&gt; · and I think that&apos;s really cool because because the State Management solution is you build it how you need it like you know we don&apos;t depend on the update cycle and in fact that&apos;s one of the reasons why I love and chose flutter was because I can have my same application on iOS 8 I can have it on KitKat and like the lowest Android and not have to use the libraries or depend on any interesting point right one of my setting points that I I had at one of these Android conferences because I think I am an Android developer I I look at the crowd of people and these Android comp go I was you you know what I mean I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:30&lt;/strong&gt; · feel like I&apos;m not trying to like there&apos;s no sort of realization that if you want to use it use it if you don&apos;t want to use it don&apos;t use it right I don&apos;t care but I want you to try because once you try then you might realize what the benefits are right like hot read and H sty I mean oh my God like I was waiting&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:45&lt;/strong&gt; · like I spend so much of my day waiting for GR Bild to have no idea right that a pcture over like a a programmer at his computer desk with a skele it&apos;s a skeleton you know like with a cup of coffee it&apos;s c for like me most of the days I&apos;m just sitting there just just just waiting for a progress bar right it&apos;s waste of time like you&apos;re not learning or doing anything exactly exactly for though too it was really bad&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:06&lt;/strong&gt; · and then suddenly like you get this hot reload and hot restart thing and whoa do you mean I just hit save yeah and I click on the button again and it performs different code I&apos;m like mind blow without nating to the screen and it&apos;s like you get to stay you don&apos;t have to like refill out the form you can just update the field yeah I mean like the this kind of like process I mean I have to say one of the things that&apos;s so underutilized and I want to tell this to people because a lot of people don&apos;t know re reassemble reassemble is a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:35&lt;/strong&gt; · function on state for widget that gets called when you hot reload only during development right meaning that you can make your widgets react during development that how they would not react during so you can have it reload a widget or or an object from your backend database right so you don&apos;t have to do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:53&lt;/strong&gt; · hot restart so most instances where you oh we didn&apos;t get the right state or it&apos;s got this old thing oh I have to hot restart right reassemble fixes those issues right so so that&apos;s what that people need to think about when when you get those issues reassemble is your first thing to go to and say what can I do during reassemble that&apos;s going to make my development life exper you know my development experience of building this form or building this sort of frontend system easier that&apos;s that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:16&lt;/strong&gt; · that&apos;s what it comes own I haven&apos;t used it before I mean this is it right like like like one of these underutilized features I tell you can potentially save a lot of time yeah cuz at least at the very minimum you could just start with like just calling the init method over again CU that fix a lot so so all you need to do is a lot of time is cancel whatever streams and restart your streams whatever you&apos;re doing in a knit so cancel them and then call a nit State again disposing it yeah yeah disposing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:42&lt;/strong&gt; · in it essentially right but obviously there&apos;s there&apos;s um side effects to doing that in terms of you&apos;re calling the super classes and you don&apos;t know if your mixings and whatever else is going on right but my point here being that it&apos;s a method right and you get to do whatever you want in that method method and it happens gets cold during hot reload on every widget and every state for widget speaking of which I think it&apos;s a good time to mention that yes there is K debug mode now constants that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:05&lt;/strong&gt; · people don&apos;t realize and you can check to see if you&apos;re in profile mode or release mode so also in your code a lot of people don&apos;t know about that and just like you can do if you&apos;re on web and stuff like that so so it&apos;s K debug right it&apos;s lowercase k capital D debug right one word yeah and that&apos;s the that&apos;s saying no if you&apos;re in debug oh my god do you know how how I was doing it before for years before that I can&apos;t when did that come out like a year ago now I know it&apos;s a while ago the no yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:30&lt;/strong&gt; · yeah you do theer you set booing flag and you return that booing flag so because asserts are only kept for debugs you know if it returned true you&apos;re in debug now that was the way of doing it I was so frustrating and then suddenly to to have a all it is is an environment variable right it&apos;s a dark Define essentially right they get sent across and it&apos;s detected but yes is the code&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:50&lt;/strong&gt; · which you WP in that if statement being uh cut out like optimiz because I think with assert the actual code which you have in there is being thrown away now that is a good point um I believe you know what you just you have just brought up a point there so so during compilation um the string from&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:09&lt;/strong&gt; · environment I believe is a constant expression it should be but I&apos;m not sure now you mention it you know so so one of interesting points to make which is something that actually summed to me the day was if I build my flutter web app with skier right you do the extra dark defined Flags whenever&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:28&lt;/strong&gt; · build in release mode how I&apos;m not specifying that flag when I run it right so how does it know I usually specify it when I do release mode no but the key Point here is is no no no what I&apos;m trying to say is once it&apos;s been built oh once it&apos;s been built gotcha so so this is my point so so the dark code when it runs if you know if you look at like the way that works right in the in the flat web framework it does a it literally&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:51&lt;/strong&gt; · does one of these like string from environment variable is this environment variable for the do defined set yes then choose this code path else choose this other code path right yeah now the key here is that those things are done as overwrites to the normal process environment variables yeah right I I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:10&lt;/strong&gt; · might be wrong but that&apos;s from what I remember on IOS and Android when you and especially Mac OS when you run a uh a defined variable it actually up updates the generated config with that new variable set to whatever value so then at release mode it just reads from the envirment yeah yeah but my point here is it&apos;s part part of the compilation process is compil into your application right so so so during deel it&apos;s one of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:33&lt;/strong&gt; · those interesting things during development mode it&apos;s Dynamic but during release it&apos;s it&apos;s baked in right that process but my my interesting point here is because it&apos;s baked in does it get the tree shaking side of the compiler right does it does it strip out those copath that&apos;s not no longer ever going to travel down because those are baked in I&apos;m not sure and I think that&apos;s what no was getting I&apos;m not sure about that kind interesting yeah it would be nice if they could also throw in uh like you know the version that your app is on from the inside the compilation progress&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:02&lt;/strong&gt; · um compilation process for you know your variable I know they do that for the release mode but yeah so you know there&apos;s build uncore version I think is the widget sorry is the is the package and what it does no no no I mean yeah yeah I&apos;ve done packages for it but what I mean is like I think it&apos;d be cool if uh just like you can read from environment it&apos;d be cool if you could do uh get the version from environment like cuz they have that at the at the bill time um sure I mean I mean but you can I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:30&lt;/strong&gt; · mean you can set that yourself during your sort of any c environment that you&apos;re doing right I mean like you&apos;re not this for the application to read itself like so you can like show like what your change log and stuff like that without having to use a package for it that&apos;s what I was meing yeah I mean it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:47&lt;/strong&gt; · the thing right like like the point here is not about that right I think the point here is what do you want the framework team to man manage and own because they can&apos;t do it all right so it&apos;s nice to say oh we want all these features but then they have to be owned by someone at Google right and and that takes Staffing and other things right away from potential other things that we need doing like bug fixing and other things right and desktop and all these other things that we want from from flatter right like we it&apos;s like we&apos;re like a bunch of kids and KN and FK going I want I want I want I want my food I want you know and it&apos;s like it&apos;s like so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:18&lt;/strong&gt; · that&apos;s where that that kind of community and packages come from right being able to do these things ourselves and that&apos;s one of the nice things was going back to that like that freedom that flat gives like these are all things like you want to you want to put something in the build process make a package do something during the build process you can do that no problem and and a good example of this is um I&apos;m just looked it up now it&apos;s it&apos;s build uncore version um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:40&lt;/strong&gt; · yep that&apos;s the one and what it does is it takes the package version from your yaml during the build process and puts a constant value in your app as in a file then you just read that constant right so every time you build your app you get the right version inside your um inside your apps without having to Cross Key difference right is if you were to do this with um package&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:03&lt;/strong&gt; · info package info gets it from the Android package information or the iOS bundle information right which means it has to cross the bridge and it has to go away and it has to do all this other stuff again it&apos;s to be honest the Zero Performance impact on your application be so NE well it&apos;s not zero but it&apos;s negligible because you&apos;re doing it once&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:23&lt;/strong&gt; · right yeah you&apos;re doing it once you display your about screen or your info screen or your menu or whatever it might be where you display that version you&apos;re doing it then right what you&apos;re not doing it is 60 times a second so you not want to use this platform Bridge as you know serialization distalization process you want to be using that thousands of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:40&lt;/strong&gt; · times a second hundreds of times a second because of course you&apos;re going to get lack because now you&apos;re trying to squeeze something in one frame of 16 milliseconds and suddenly like you easily going over that limit now of course asyn in away isolates won&apos;t go into there right um yeah we&apos;ll be here for for days yeah I think um wrapping up the architecture part correct me if I&apos;m wrong um for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:03&lt;/strong&gt; · people deciding and thinking about architecture and State Management a few things to take away from this episode is keep it simple if there&apos;s something uh which sounds complicated requires a lot of Bop Cod and just doesn&apos;t feel right don&apos;t do it use something which works for you which is simple and most importantly you understand if you write code which that you don&apos;t understand who else going to understand it um also Flor&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:31&lt;/strong&gt; · is very data driven very reactive so maybe Shifting the Viewpoint from UI to the data when um architecting applications might make sense because instead of looking at okay what is happening where look at the data how is it flowing where is it coming from where is it going um did I miss something uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:53&lt;/strong&gt; · just yeah definitely be uh yeah just like choose it based on the data flow and not because of you know because it&apos;s popular at the time for sure I mean I mean again like don&apos;t start doing like second or third order the functions and like functions return functions every return functions because you&apos;re going to you&apos;re going to get into a problem somewhere don&apos;t don&apos;t over complicate things right yeah just real quick I wanted to check and see you know what the general Vibe was I know right now A lot of people are trying to decide between layers and features or folders by feature or folders by layers uh what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:23&lt;/strong&gt; · guys do uh for me personally I love having a UI folder a source folder in each one of those like in the UI folder I have a folder per screen and inside each screen folder I have an entry point and probably a common folder and then a main common folder for all widgets in the backend has classes models utilities and stuff like that so what about you guys well um I think I think I&apos;ve kind of sort of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:49&lt;/strong&gt; · I&apos;ve done it both ways right and I think you can even mix the two so you can have like um sort of feature level stuff if you want to but I kind of feel like if I&apos;m going to separate all my let&apos;s say I was doing uh NVC right I have all my models in one place all my views in one place all my controllers in another place maybe that makes sense right right doing it that way however if I&apos;m G to do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:10&lt;/strong&gt; · things with the way I feel is normally where I have like a screens folder or right that has like then a sub subfolder for each one of my main sections of my app C screens maybe top level things and they can have any of the the widgets and things related to that in them right includes if I might have blocks there might be in there as well right they they related to those screens right then I would have a widgets folder at the top level which is like generic widgets so if I have a toolbar for instance that used across the entire every screen then I want that above that the specific&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:41&lt;/strong&gt; · screen layer then I&apos;m putting that in that widget s essentially common area um I also normally make a backend folder which is the back end of the application and anything this is my layering right so I&apos;ll put then inside there my services I have a Services sub folder repeat repost subfolder and I have my service and repositories so I can keep if I wanted to I can literally&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:02&lt;/strong&gt; · lift out that backend folder into a into a separate package and now I&apos;ve got a backend package that&apos;s completely separate to my front end UI if I wanted to right but it&apos;s mainly about just knowing when you cross that barrier right like you have imports from a completely different top level of your app you&apos;ve crossed the layer right so you can kind of you can visually see that a lot of the time so you can it&apos;s just more of a hint than anything else I mean um yeah of course you have so for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:29&lt;/strong&gt; · me um I used to be with different layers before but over time I shifted more and more into um grouping features together because for me most often not when work on an application instead of working on a layer I usually work on a feature and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:48&lt;/strong&gt; · instead of just staying in the layer I usually cross the boundaries between okay I have to adjust this model I have to also adjust the UI and maybe some backend calls so usually when being in this work context I&apos;m usually in the context of one feature and I have to access most of the parts so also as an example when using fir store um you don&apos;t have that much um backend you want&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:12&lt;/strong&gt; · to separate maybe having all that in one folder for example um for me having a feature folder in the feature folder having a UI folder a network folder maybe a store folder which may extend some Base Class which is in a top level layer separated um folder makes sense&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:32&lt;/strong&gt; · because when touching that specific feature I can touch every class and stay in the same subd directory I did I did just think when you were just saying that I did actually think one thing I do do that is make an app folder and that contains my top level like theme or also I have an app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:49&lt;/strong&gt; · dot which is normally my top level entry point which is like contains the material app and any of that Associated materials because main I feel like main should just be your entry point Main is just your main method that&apos;s it right I feel like like it if it needs in your main method you need to go off to the back end and initialize stuff and then and then bring up your UI so then you&apos;re calling the app but I feel like don&apos;t just pile lots of stuff into your main do dot you know it actually&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:14&lt;/strong&gt; · comes from um I don&apos;t believe it&apos;s a problem anymore but is be a problem with Imports when you had an import from two different a local as well as a package part used to conflict and used to have same type problems uh I&apos;m not sure if that bug was solved now but yeah it&apos;s solved I don&apos;t have that anymore but but I kind of learn myself into not doing that main problem because it would always happen when you had stuff in Main and you try and use it elsewhere and you&apos;d get that issue because locally because Main&apos;s the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:39&lt;/strong&gt; · entry point it would always have everything local right and then if you try and import with the package path later it&apos; be two different type instances but but either way like like you normally I he flavors right I mean like you talk about flavors in architecture right like youd have I normally do this main de maincore Dev maincore prod maincore whatever each one of my different flavors right testing whatever it might be and then I can then spin up the main method can spin up&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:04&lt;/strong&gt; · different uh constant values based on that so I can now tell my application oh it&apos;s in in the dev mode or in the production mode or in this so I can have different behaviors so I can have more logging perhaps when I&apos;m in the dev mode or I&apos;m using the dev back end rather than the production back end and you can make those sort of differences it&apos;s also very important part of architecture&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:25&lt;/strong&gt; · yep well Simon thank you so much for being on our podcast today uh we definitely want to chat in the future more about this we could go on probably for a week on all the different topics on this but uh yeah I just want to thank you so much uh for you know taking the time here talking about the subjects and uh is there anything that you would like to promote while you&apos;re on oh now you dropped me in it all right let me you can cut this bit out but yeah yeah um yeah of course like so we&apos;ve got the upcoming hack 20 uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:57&lt;/strong&gt; · soon to be released uh we&apos;ll be releasing more information on the website soon where lots of people heavy heavily s working around the back end here at the moment trying to get things ready for the day so um yeah if you&apos;re if anyone&apos;s interested in the flutter hackaton we did it last year good success uh obviously it&apos;s all in person 70 locations worldwide 24 24-hour live stream which we will not be repeating this year but we will be doing some streaming some live streams um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:24&lt;/strong&gt; · yeah so if you&apos;re interested and you want to be involved or you want to sign up to hackathon go to the website flutter hackathon docomo ified when when we get more information going out the door awesome yeah and then also flutter Community make sure to reach out there too I know you&apos;re one of the heads of it too yeah I guess uh Norbert what about&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:46&lt;/strong&gt; · you oh um actually real quick I&apos;ll cut this out but and where can they find you Simon online uh best place to uh I guess get hold of me is on Twitter uh Twitter uh my Twitter handle is Dev Angels London uh kind of regret doing my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:01&lt;/strong&gt; · company name because I was using my company name to begin with but now it should be personal but you know that&apos;s what it is and um I think that&apos;s probably I think that&apos;s probably the best best sort of communication through that you&apos;ve also got um I mean I&apos;ve get people adding me on LinkedIn I just don&apos;t accept anyone on LinkedIn unless I know them yeah it&apos;s I got to draw the line somewhere because I&apos;m not joking i&apos; like again following people back because I&apos;m not joking I get like 10 follows down I don&apos;t know these people I don&apos;t like people it&apos;s kind of interesting that people think that um they see that I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:32&lt;/strong&gt; · following someone and they oh he he he must know this person right like like like that&apos;s G I&apos;m giving someone else um like Authority I know I&apos;m I&apos;m sort of up in them so I feel like I can&apos;t to start I got stop doing that right and it&apos;s not out of M or anything like that just I can&apos;t the plus I&apos;ve turned off the that I can&apos;t I can&apos;t there&apos;s too much yeah what about you Nobert where can they find you um as usual on Twitter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:03&lt;/strong&gt; · um yes mainly Twitter I&apos;d say I&apos;m always on the podcast so you&apos;ll hear me next week or the week after I don&apos;t know when it&apos;s next going to be um yes what about you Ro yeah you can always find me on Twitter Ro Davis and GitHub and um yeah we&apos;ll have links to everything that you&apos;ve seen in the description and the show so uh make sure to you know rate US&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:26&lt;/strong&gt; · on iTunes it really helps and Stitcher Spotify wherever you listen to us and want to thank you so much for listening today uh we look forward to seeing you in a future episode coming soon thanks thanks thank [Music] you&lt;/p&gt;
</content:encoded></item><item><title>Pro Putt by Top Golf feat. Jason Clevering</title><link>https://rodydavis.com/podcast/creative-engineering/pro-putt-by-top-golf-feat-jason-clevering/</link><guid isPermaLink="true">https://rodydavis.com/podcast/creative-engineering/pro-putt-by-top-golf-feat-jason-clevering/</guid><description>An interview featuring Jason Clevering discusses the development and business strategy behind &quot;Pro Putt by Top Golf&quot; using virtual reality technology on Oculus Quest.</description><pubDate>Tue, 21 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Pro Putt by Top Golf feat. Jason Clevering&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;TqAEM9Y2cuQ&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Pro Putt by Top Golf&lt;/p&gt;
&lt;p&gt;https://www.proputt.com/&lt;/p&gt;
&lt;p&gt;Norbert Kozsir - @norbertkozsir&lt;/p&gt;
&lt;p&gt;https://twitter.com/norbertkozsir&lt;/p&gt;
&lt;p&gt;https://github.com/norbert515&lt;/p&gt;
&lt;p&gt;Rody Davis - @rodydavis&lt;/p&gt;
&lt;p&gt;https://twitter.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://github.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://youtube.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://rodydavis.com&lt;/p&gt;
&lt;p&gt;Our podcast player:&lt;/p&gt;
&lt;p&gt;https://rodydavis.github.io/creative_engineering/&lt;/p&gt;
&lt;p&gt;Follow on Twitter:&lt;/p&gt;
&lt;p&gt;https://twitter.com/CreativeEngShow&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · welcome to creative engineering we have a special episode today with Jason clevering how you doing Jason doing great thanks for having me on the show and we also have my co-host Norbert how you doing Norbert hey I&apos;m doing pretty well too so today we&apos;re going to be talking about prop put for uh by top golf for Oculus Quest that is coming out soon and uh Jason you are um what part of the team are you on so I&apos;m I&apos;m the president of the company and and essentially what I um work with the team is how however I can help dreams happen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · and bring them to reality through uh through different aspects of the business um and then just general Partnerships um such as the one with top golf uh and other you know kind of General um business to-do lists in terms&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:52&lt;/strong&gt; · of development so whether that comes from financing to you know something strategic um or looking down the road at at what we can do um in current climates as well&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · that uh that might be viable not only for us as a company but also for VR in general as it grows oh wow um so what what is your background with uh VR or just development in general um in terms of development well VR is is so new there&apos;s not a lot of of people out there that have been around long long time even though there&apos;s there&apos;s been different things that have dabbled in it you know from Nintendo on in in VR to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39&lt;/strong&gt; · where we are today with something like the quest um my background is primarily in entertainment um in traditional specifically film and music um when it comes to development of games uh I did&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · study from a production standpoint uh at USC in California did uh worked on some beta teams through uh through my University years and stuff like that but at heart I am an Enthusiast um I&apos;m somebody that started off with pong on a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:17&lt;/strong&gt; · black and white TV and I couldn&apos;t find any of my friends that were good enough so I actually it&apos;s the only thing I can do ambidextrously is play Pong so I&apos;d play against myself for many many hours and then my dad would be like here take this tennis rack and play a real sport kid kick me outside so uh but no I I I went&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:39&lt;/strong&gt; · through through the entire evolution of of video games that kind of says a lot about my age and I&apos;m I&apos;m definitely the the senior one at uh at prop put at Golf Scope or the parent company so uh so yeah I&apos;ve seen it all and and just always always have approached Tech technology and gaming from some you know this these very childlike um amazement eyes and then this very keen Sense on how can you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:12&lt;/strong&gt; · involve the masses and then at the end of the day turn it into a business so uh Golf Scope like I said our our our parent company has really been that platform since late 2019 for for me to really work closely&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:30&lt;/strong&gt; · with our founder Ryan Engle and uh and and develop into what we&apos;ll release here in a couple of days on the Oculus Quest wow that&apos;s amazing pretty cool where are you guys located so we&apos;re in Austin Texas um and it&apos;s you know an interesting mix of us that go from all level of golfer um all level of ability&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:57&lt;/strong&gt; · in the tech world um in the design world in the business world all of that that we are this this Band of Brothers that somehow or another showed up you know in the same location at the same time to do some rather spectacular things and lend all of our talents to uh to a vision that uh our&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:22&lt;/strong&gt; · our founder Ryan has really committed to since the first app that he created for the company called gol scope did a lot of your team like how much of your team do you think um was like their first experience with VR or was it other people had just game experience or was this all um professionals so it&apos;s other&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:42&lt;/strong&gt; · than Ryan it&apos;s all our first um experience developing something for VR and uh definitely some interesting challenges I think the biggest rule of developing for VR is get it in the headset you know it&apos;s with with VR it&apos;s such an emerging um realm and I say that in in a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:05&lt;/strong&gt; · number of different senses it&apos;s it&apos;s emerging and reemerging at the same time in the sense that there are a lot of people in this world that have never experienced VR and it&apos;s not something you can describe you know I can&apos;t I can&apos;t put it into pros and you understand what it is like to stand on the beach course and watch birds fly overhead and I can&apos;t put it in a a video&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:30&lt;/strong&gt; · and have you have your senses kind of start playing in VR meaning that it fills in the blanks where it almost feels like the sun is shining on you or the wind is blowing and things like that so VR truly requires somebody to be in the headset and then it&apos;s reemerging at the same time because you have technology like the quest that is getting rid of those firsttime ill&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:55&lt;/strong&gt; · experiences that people have had in VR in terms of I give you a perfect example I&apos;m I&apos;m the best example of reemerging into VR my first experience in VR was with the psvr and Grand Turismo I&apos;m a huge racing fan so I was like oh the first thing I&apos;m goingon to do is strap myself into a car and like race through&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:17&lt;/strong&gt; · these European streets at like you know 260 kilometers an hour okay well that worked for about four minutes and then I tore the headset off I ran in the bathroom I vomited for 15 minutes and then laid on my back for 3 hours and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:32&lt;/strong&gt; · swore I would never do VR again it&apos;s like your first tequila experience it&apos;s like no I&apos;m never drinking that like you smell it and you start to get sick and I&apos;d like see a VR headset in somebody&apos;s room and I&apos;d be like oh God where&apos;s your bathroom so I think there&apos;s that reemergence so when it comes to development a lot of us are in that same&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:54&lt;/strong&gt; · realm where we&apos;re trying to cast off anything that was bad in terms terms of VR at the same time we&apos;re trying to put our headset headsets on as often as possible so that we understand what we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:10&lt;/strong&gt; · we all have experience in 2D and 3D doing now when you put it in VR now you understand what what you truly have the power of doing I think more so than any other entertainment platform and that&apos;s truly surrounding somebody being able to commit to their emotional space as much as their physical space and so we&apos;re all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:37&lt;/strong&gt; · just you know kids trying to figure out how to make the cool stuff one thing the Oculus Quest got right is the six off tracking you know having the ability to move around a room versus just like the Oculus go where you&apos;re just doing like basically what cardboard did um like that was my first experience was the just the head tracking and it was great but I got so sick there&apos;s just something about like when you want to turn your head left and right that spatial awareness in VR that takes it to the next level and I remember getting so sick or just like you know for me the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:08&lt;/strong&gt; · Oculus Quest was one of the first devices that I almost did notice the pixels like you can if you start to pay attention but they really disappear when you&apos;re not paying attention versus like the cardboard where they&apos;re just it so obvious when you had a lower resolution phone or you know things that weren&apos;t made for it as well as just you know experiences like like you said like the psvr where it&apos;s just like you know this is it&apos;s cool but it&apos;s like Oculus Oculus&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:33&lt;/strong&gt; · shows that can be so much better yeah and I think being able to cut the cord is so important it it doesn&apos;t matter how immersed you are the second you turn and you feel something kind of yank you back it Yanks you out it&apos;s not just like oh you know like okay I&apos;m I&apos;m at the my limit or you know or you feel it on your shoulder or something like that there&apos;s always going to be something that&apos;s breaking the VR wall and pulling you out of the experience so with the quest not having that you truly feel immersed at&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:02&lt;/strong&gt; · that point in time it&apos;s like there&apos;s the the you&apos;ve lost the world around you because there&apos;s nothing literally tethering you to the world around you so yeah now I&apos;m literally breaking physical walls exactly with the quest you gain so much flexibility because I mean in golf you don&apos;t have to play in your room you could go outside in your garden and actually stand in the the grass and play golf there just open so many doors you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:31&lt;/strong&gt; · do which you couldn&apos;t just bring your computer to like you wouldn&apos;t want your to bring your big beefing machine and find some sort of a power just to just to play some VR yeah no I think that&apos;s in in prop put one of the days during development that I really kind of like got me is I was you know the great thing about prop put is and how we&apos;ve developed it is that you Orient yourself&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:59&lt;/strong&gt; · in your guardian basically in the center of it and once you Orient yourself you never have to move really I mean if you need to turn you can use the joystick to turn but it&apos;s not like you need to walk anywhere or like do anything so you can really just stay fixed and you can putt&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:17&lt;/strong&gt; · anywhere um so I kind of went through a bunch of things like oh you know what I&apos;m going to putt in the bedroom and then I&apos;m going to put on the bed and then I&apos;m going to put in the shower without the water running but one day I was putting in the kitchen and I had a window open there was a pretty significant Breeze and I was standing on the course I was putting and the breeze hit me and it was one of those things that it was just it just added something&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:42&lt;/strong&gt; · to it that made it even more real and even though you knew it was the real Breeze there was this great moment where VR owned the physical space it&apos;s like I&apos;m now going to use this to my advantage you know to this person even&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:00&lt;/strong&gt; · deeper so yeah having things like feeling this actual heat of the sun on you while you&apos;re putting or feeling the wind and things like that it&apos;s just it&apos;s kind of fun to experiment the and the quest lets you do that the quest truly lets you put yourself into environments that&apos;ll that&apos;ll expand it even further without having to buy additional Hardware like you can use nature to add to the experience so that&apos;s that&apos;s been fun so When developing this game um I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:27&lt;/strong&gt; · know you guys um some of you guys have golf experience were you inspired by any other golf games when you started building this or was this mostly just like you know we have this Vision with top goof and just want to you know carry it out I mean again you know not to you know show my AARP card or anything like that but I mean I go back to golf on Atari 2600 you know and it&apos;s it&apos;s always been one of those games in a digital sense that you feel a little bit like my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:02&lt;/strong&gt; · knowledge of the real sport is going to translate fairly well here even though I&apos;m hitting a button you know whether it&apos;s just decisions on okay I should go for this shot or I should lay up or things like that that that that do that um but I mean the the Pinnacle for for me and I think you know few guys on the team here would definitely be any of the Tiger Woods games that happen on on Xbox&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:27&lt;/strong&gt; · or Playstation they just they had one feature in there that was just it like it hooked you I mean that&apos;s you watch a movie like Tin Cup you know and he talks about he&apos;s trying to explain what golf is you know and he&apos;s like you know that you know that tuning fork in your loins goes off just one time you know that one perfectly struck shot no matter how many horrible ones you&apos;ve hit before you&apos;re coming back the next day because you you&apos;re like oh I want to feel that again so in Tiger Woods games it was like that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:59&lt;/strong&gt; · heartbeat like you&apos;d hit a shot and all of a sudden it&apos;d like go into like this weird like graphics and then like the controller started pulsating and you were like oh yeah and then if you were playing against somebody and they&apos;d hear it they&apos;re like oh come on not again you know it was just like it it was immersive as golf so that was the first time that you really felt the emotions&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:23&lt;/strong&gt; · of golf start to trickle into the digital realm you know with that that that heartbeat p in that controller it&apos;s just like oh I just want to feel this over and over again yes it&apos;s addicting yeah so um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:38&lt;/strong&gt; · building a golf game itself is pretty hard I can imagine but building a golf game in VR is even harder what were some major challenges you faced and how did you solve them well again it&apos;s it&apos;s all about getting in the headset so a lot of it was you know on paper that that seems&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:00&lt;/strong&gt; · like that would translate into golf emotions but until we did it in the headset sometimes it works sometimes it didn&apos;t I think one of the the biggest was to get you immersive in an environment take you somewhere take you on a journey that uh that I think is very important in golf if you think about golf as a sport in you know in an&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:26&lt;/strong&gt; · urban setting I lived in m Angeles for many years in La first off everybody drives everywhere like literally I knew people that would drive a block away and to go to a friend&apos;s house you&apos;re like really you can&apos;t walk a block so you know there&apos;s a there&apos;s an old song in the 80s called Walking in LA and it was basically nobody walks in La um and so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:50&lt;/strong&gt; · the only time that you your feet ever really touched grass in La was when you go to play golf so it&apos;s truly like one of the only times you get to like play a sport where your feet are on Turf so it was really important that we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:08&lt;/strong&gt; · were able to replicate that Sensation that okay I feel like I&apos;m on a golf course when you&apos;re on one of our putting courses but I think the the biggest challenge that we&apos;ve gotten right where most games have not gotten this right is the sense that when you hit a putt and the ball rolls it looks like a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:32&lt;/strong&gt; · that&apos;s what I intended to do and B the ball really is rolling the physics are so precise and so real that it was that was the biggest thing that we we decided we needed to do is like if this ball doesn&apos;t feel like a real golf ball and a real golf ball rolling and if it hits the hole on the Edge at a speed if it doesn&apos;t start to go down and kind of come back out out like a real golf ball would then all we&apos;ve created is a video&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:03&lt;/strong&gt; · game and I think uh that&apos;s exactly what we haven&apos;t done we&apos;ve created a golf experience and it just happens to be that you&apos;re only using your putter but that doesn&apos;t mean that those emotions and those frustrations and the Elations&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:20&lt;/strong&gt; · and all of that all the shuns don&apos;t come into to play and I think that was that was our our biggest challenge and our biggest achievement through it all what was the thing that you were probably most proud of like was it because I know we talked about you know porting that uh pretty complex engine was uh like cuz for me when I first played prop put&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:40&lt;/strong&gt; · that&apos;s the thing that got me was it did feel like a golf ball because you know it&apos;s something like you like when you play like a pool game online if the ball doesn&apos;t roll like a pole ball it&apos;s like it just feels so silly like it doesn&apos;t it just feels like a toy doesn&apos;t feel real yeah it feels like a cat toy sliding across the ground you know like it&apos;s like it&apos;s just not going to work you&apos;re like uh no I don&apos;t want to hit that thing again cuz I don&apos;t know if it really is doing what I intended it to do so yeah no I know it coming from how&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:11&lt;/strong&gt; · did you you know combat like the differences between because I know the the pro version you know has way higher physics and then the regular mode is a little bit easier how do you make those trade-offs between you know the you know the really hard simulation kind of style and then the kind of relaxed having fun yeah I you a lot of it is you just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:32&lt;/strong&gt; · thinking about it from a golf perspective you know if you took a guy that never or you know 30 handicapper that you know has played golf three times you&apos;d never take him out to the black horse at Beth Page you know it&apos;s like one of the hardest golf courses in North America and you&apos;re like that person&apos;s just GNA have a miserable miserable time and probably never play golf again like that&apos;s the worst sport ever so I think with amateur mode where&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:00&lt;/strong&gt; · the physics are all the same we give you the the putt line so that you can see where you&apos;re supposed to putt so we give you you know a few things to to help you out through this but um in no way are we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:17&lt;/strong&gt; · making putts for you type of thing we haven&apos;t gamified it to that so we wanted people to be able to get in there and and feel what it&apos;s like to make a 30 foot putt you know whether there&apos;s there&apos;s a satisfaction whether you make a two-footer or you make a 100f footer there&apos;s a satisfaction there but seeing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:36&lt;/strong&gt; · that ball drop that goes from Satisfaction to I use this word often Elation because that&apos;s just like the ultimate satisfaction and seeing a ball like dive over the edge of the hole into the bottom of the cup that&apos;s just Elation what we then wanted to do is we didn&apos;t want anybody to sit there and go well that&apos;s just a video game and especially golfers and so in PR mode I mean there&apos;s there&apos;s no assistance in any way shape or form I mean it it&apos;s a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:04&lt;/strong&gt; · game of millimeters like it is on a real golf course if you don&apos;t hit that putt exactly right if you haven&apos;t chosen your line and hit the right speed and all of those things it&apos;s not going in you know just like real golf so we wanted you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:20&lt;/strong&gt; · know to give that experience as well where you know okay I can take this to the the Pinnacle of you know where goes into true simulation of of of what the ball&apos;s going to do when you when you strike it with a putter so the trade-offs were were were pretty easy um you know that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:41&lt;/strong&gt; · you know we wanted people to come back and enjoy it and feel all those great things and then we also wanted to treat it like putting you know most people in this world have struck a putt in real life with a Putter and a golf ball you know and it&apos;s whether it&apos;s on a miniature golf course or on a PGA t tour course you you probably have have struck a putt even with a plastic putter as a 2-year-old so people know what that movement is um the learning curve for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:09&lt;/strong&gt; · how to hit a putt successfully is pretty shallow just about anybody can do it you can take a two-year-old you can take an 80-year-old and they can strike putts and not have to have some crazy physical prowess to do so but to master it is impossible and so it was important in PR mode that it was that same level of impossibility to master it you can get really good but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:34&lt;/strong&gt; · you&apos;re never going to master it I mean any golfer out there is this I mean you look at somebody like Jordan spe number one putter in the world like I mean the guy couldn&apos;t miss from six zip codes away in any tournament and a year later he&apos;s like out of the top 100 I he couldn&apos;t he couldn&apos;t Kick It In from an inch away you know and that&apos;s what putting is and it&apos;s going to reward you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:57&lt;/strong&gt; · many days and then it is going to haunt you many nights so that was what was important in in Pro mode in Pro put for sure that you got that that that same kind of spectrum of um reward and hatred&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:13&lt;/strong&gt; · out of it I guess in the first part of the development did you kind of face like when you&apos;re like gaslighting it to people like um oh well is this going to be like a putt putt game like stuff like that yeah I think a lot of people immediately when you just say putting game that&apos;s our thought is windmills and colored lights you know and to you know to their defense that&apos;s all that&apos;s been out there ever really you know nobody&apos;s really approached it like we have from trying to mix that that golf with game&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:44&lt;/strong&gt; · um aspect with putting but leaning hard into the golfy motions and the challenges and the you know the social aspect where you feel like you&apos;re playing golf and you&apos;re not playing putt putt those are big big differences but you&apos;re still using the same Club all the time um but no it was it was always or&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:12&lt;/strong&gt; · we get the request of you know are you guys going to do miniature golf courses and you know never say never you know but I think we&apos;ve found something very unique in prop put that speaks to everybody that also feels like a new experience like I&apos;m doing something I haven&apos;t done before with a putter when you get on the putting courses and you go okay now I need to if I am a golfer I have to really approach it as a golfer and if I&apos;m a gamer I can really try some&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:41&lt;/strong&gt; · outlandish things to to dial this in like I like it or just to be able to brag to my buddies that you know I hit a 130 foot putt off a rock you know so yes it&apos;s uh yeah I mean like for me it&apos;s kind of nice though in a way that people do have that experience with put but that it&apos;s so accessible that you can have that conversation to be like you know it&apos;s an easy sell to be like yeah well if you like putt putt you&apos;re going to really love this because there&apos;s you know like when you&apos;re playing the game you get to like you know putt under a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:11&lt;/strong&gt; · waterfall and in the desert and there&apos;s it&apos;s so much more immersive than a a pup course could ever be yeah absolutely so what was your um experience working with Top Golf on the game so far I mean it&apos;s been it&apos;s been really great we uh we began talking to them um a little over a year ago and uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:35&lt;/strong&gt; · you know we were the easy thing about it is we have the same mission in this all and that&apos;s to grow the game of golf um to do that through entertaining ways to do it with technology and all of that so we shared the same vision at the end of the day so that that always makes it easier for for um two Pary to come&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:58&lt;/strong&gt; · together and and and work on something for for a common goal so that was great um you know it&apos;s it&apos;s been challenging um the last couple of months as most businesses have you know is just trying to understand where we can go you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:16&lt;/strong&gt; · we had a lot of hopes and dreams that are going to you know either be put on hold or have to be reinvented and I know that everything that we&apos;ve done together so far that we&apos;ll be able to overcome that um so it&apos;s it&apos;s a new&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:35&lt;/strong&gt; · relationship um everybody is excited there everybody is excited on our side and you know when you share the same same vision and mission statement it it it allows you to do a lot of things um together very successfully so we&apos;re looking forward to that that opportunity over time did the virus kind of accelerate the growth or the development of this game um in terms of accelerate the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:04&lt;/strong&gt; · development no we you know it&apos;s working in the digital realm and uh with a smaller team it was one of those things that we we had the vision it was pretty much just execution and in development certain things no matter if you work 24 hours a day just take time you know and then uh then you break things and then you got to build them back up and you know things like that so typical development timeline so the the pandemic&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:33&lt;/strong&gt; · didn&apos;t affect us in that way I think if it affected Us in any way it&apos;s it&apos;s how are we going to get this message out to the masses about VR um again like I&apos;ve said you you in order to get somebody excited about VR that&apos;s never been in VR they have to experience having a headset on them and it&apos;s the the one thing I think that Oculus has has found in the in the positive you know is that the quest is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:06&lt;/strong&gt; · incredibly popular so it&apos;s still very difficult to buy one of these I I&apos;ve always got a like retail tracker going like I get like hit by Walmart at 2:30 in the morning it&apos;s like oh it&apos;s available and I&apos;m like you know if if you&apos;re not online and in 10 seconds it&apos;s gone basically so so even trying to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:27&lt;/strong&gt; · communicate out for somebody to get a headset even if they were going to just take this blind leap of faith is is difficult so the best way is to is to do it through events and and and things like that that&apos;s just going to be challenging now you know um somebody putting on a public headset they&apos;re going to be scared you know and uh and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:49&lt;/strong&gt; · we all have to be cautious there so I think uh I think that&apos;s probably the biggest way that this has affected us but uh again it&apos;s uh we&apos;ve got a lot of things that we&apos;re going to do in the coming months that I think will really get people inspired to kind of like I said take that blind leap of faith and go okay maybe I need to buy one of these&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:11&lt;/strong&gt; · quests it&apos;s I think it&apos;s a the closest thing that you can compare it to in recent times would be the Wii you know it was like the Wii came out and it was like every was like what is this Nintendo thing like what I don&apos;t know what it is you know and then you saw like Grandma B and like jumping around and going like that you&apos;re like okay maybe I should try this thing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:34&lt;/strong&gt; · you know and then you tried it and you&apos;re like okay now I&apos;m breaking a sweat four times a day trying to like play bowling or whatever you know and it&apos;s like it&apos;s it&apos;s going to be that type of thing I think with just a number of different experiences and hopefully prop put is one of those where somebody goes that looks like it&apos;s a lot of fun that looks like something I can do so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:57&lt;/strong&gt; · maybe I should make the investment and go down that road so I know you know you&apos;ve got Facebook behind Oculus which is the greatest marketing engine in the world so if there was ever a chance for it to be uh hugely successful I think it&apos;s definitely there and I think prop put is definitely in the position to uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:18&lt;/strong&gt; · to be that first experience for people as well you know we&apos;ll all look back 20 years from now and go what was your first experience in in VR and hopefully a lot of people can say prop but talking about Oculus um as I know the game&apos;s going to be released pretty soon how has been and how is the uh cooperation with oculus like um yeah tell us about I mean&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:41&lt;/strong&gt; · it&apos;s awesome like it really is I think one of the biggest things that I&apos;ve been impressed with with oculus is their attention to detail um I think it would have been very easy for a company like them with an engine like Facebook behind them to just go let&apos;s just put everything out&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:07&lt;/strong&gt; · there I think it&apos;s it&apos;s kind of the Netflix effect right now you know Netflix has got you know like who knows how many shows on there and how many movies and it&apos;s like it it basically is like well we don&apos;t want Amazon to get it so we&apos;ll buy it we&apos;ll put it on there we&apos;re just going to like you know put a thousand shows on there because then can say we have a thousand shows well guess what 994 of them are horrible you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:33&lt;/strong&gt; · and you&apos;re like you&apos;re sitting there I like go through Netflix and I&apos;m like okay I&apos;ll give that a shot and five minutes and you&apos;re like that&apos;s horrible and you know next one 10 minutes I can&apos;t watch that you know and by that time it&apos;s like okay I don&apos;t even want to watch Netflix anymore because my my rolling the dice luck is horrible and I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:53&lt;/strong&gt; · think with oculus they could have done that they could have taken every game in the world world out there just put it on there we&apos;ve got this G you know and G well we&apos;ve got a library of you know a thousand games and and they didn&apos;t they got selective and they made the process rigorous and they made it so that you had to prove along the way that you deserve the right to develop for Oculus and especially the quest um but at the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:21&lt;/strong&gt; · same time they could it could have been one of those things where it&apos;s like well you know the Oculus is speaking and this is it and it&apos;s not they&apos;re always like pumping you up and like patting you on the back and helping you through and all of those things you know and when you have an an an engineer like our founder Ryan you know that is is so spectacular&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:45&lt;/strong&gt; · at what he does to be able to merge into an environment like Oculus and and provide them with with things that they weren&apos;t expecting that were going to be as good as they were when they showed up on their desk um I think really helped the process with us as well so it was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:04&lt;/strong&gt; · you know very seamless very very enthusiastic on all sides and uh I hope that everybody recognizes that in the final product I think um Oculus has kind of taken a similar thing that the iPhone did which honestly I think is one of the biggest reasons why I had such a big success is with the App Store like the App Store is just the same as um you know like the Oculus store where you have to have a review process not everybody can make apps for it and I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:33&lt;/strong&gt; · think the trade-off is that you get a bunch of curated apps on there that make it you know so much of a better experience when you lower the chances of you getting a bad egg a bad app of course that means there&apos;s still going to be ones that slip through but when you lower those chances you make the overall experience for especially firsttime people it&apos;s going to be a good one uh versus like the contrast on Android when&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:55&lt;/strong&gt; · almost anybody can submit even duplicates of apps and you know it&apos;s just you then start to associate the App Store with the product itself which is so sad cuz like the product and the software are two different things yeah absolutely yeah and there&apos;s there&apos;s a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:10&lt;/strong&gt; · lot of bad apples that can that can ruin the tree really quick you know and it&apos;s uh that&apos;s definitely not the case at Oculus for sure and I I really have to applaud them they they took that stance and and decided that everything that comes out of there really is as a level of quality that&apos;s surpasses everything else so I can imagine when um working&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:33&lt;/strong&gt; · with OAS and billing app is pretty important to have an MVP uh which resembles the game and shows off what it&apos;s going to be at the end pretty quickly how long did it take you to build something which is actually the MVP of proot that&apos;s a really good question because there were a few of those moments along develop M that were just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:59&lt;/strong&gt; · aha moments where we were really thinking and you know getting the whiteboards out and you know Monday meetings that were supposed to be an hour turned into five hours and it&apos;s like you know okay and then you know something came along an idea or something that just kind of you went wow that is where we want to go um I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:26&lt;/strong&gt; · think the idea of the putting course you know and really trying to translate um a golf hole into putting was really that moment that we knew okay this is going to be something special I mean beyond the physics aspect of it you know getting the physics right was was so key but it&apos;s then like you know you&apos;ve got&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:52&lt;/strong&gt; · you&apos;ve got this Ferrari and the last thing you want to do is drive it all day long on a gravel Road you know it&apos;s like we&apos;ve got to find the Great Road that this goes on I think once that first first even hole was created in uh in a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:09&lt;/strong&gt; · putting course and it was kind of cool because you know we were like okay here&apos;s a putting course and then Ryan would go in and and create a hole and we&apos;d play it and we play it and we play it so many times and then all of a sudden it was like okay there&apos;s a new You Know download the new version and like there&apos;s three holes and you&apos;re like wow I get to go from the first hole to the second hole and then when you got to like a full course and it was time to put up a score it was like okay now all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:43&lt;/strong&gt; · I want to do is beat everybody else and I think then the next one was kind of like the leaderboard and it was multiple leaderboards like we have a leaderboard for um like feet of putts so so I&apos;ll never forget the leaderboard came up the first time on the first hole on the beach course and the putt is I don&apos;t know probably 40 some odd feet something like that fairly straight and so I hit&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:08&lt;/strong&gt; · it and it goes in the hole and I&apos;m like oh yeah top of the leaderboard and I turn around look at the leaderboard and it&apos;s like 79 feet and I&apos;m like how is that possible I&apos;m like on slack there&apos;s a problem there&apos;s a bug somebody got a 70 foot Putt and then I was like wait a second I don&apos;t need to go the most direct route to the hole now I got to get creative and go how can I send it down the left side up the bank around let it come around and go in the right side of the hole and I was like Wow 82&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:39&lt;/strong&gt; · feet like top of the leaderboard and that sense was like okay now that creativity engulf that comes into play that you hear so many Pros talk about that they&apos;re so good they just get creative with their shots that came into play and that that kind of like poking&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:56&lt;/strong&gt; · the the bear like you know okay I&apos;m at the top of the leaderboard and then you&apos;re like oh man I was in there for like three hours the other night trying to get the top of the leaderboard and Jason just took it from me like oh I&apos;m back in now you know so I think the leaderboards was that kind of next moment where it was like oh yeah I just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:15&lt;/strong&gt; · I need to own that thing you know so you&apos;re going back and back and back and back so there&apos;s there&apos;s been a number of those those moments that have been really really great throughout this that uh I I I think we&apos;ll we people will definitely pick up on those um and see how much how much fun we had developing them and how much fun they&apos;ll have actually being able to play them yeah for me I currently hold the high score for the waterfall on nonpr version but that was the one I was telling you about that I got it in in two so I was I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:48&lt;/strong&gt; · still number one so yeah great thanks so now I got to spend three hours tonight knocking you off that leader board I appreciate that yeah so fun though because it&apos;s like like you said it&apos;s uh it&apos;s not just like you know you could play the the same hole multiple times it doesn&apos;t get boring because you can find new creative ways to get there and I think that&apos;s that was the killer feature for me which was the dynamic way of playing the game yeah yeah and it it really comes into play too when you you start getting into multiplayer because&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:17&lt;/strong&gt; · you can be sitting there going okay just like real golf it&apos;s like okay I&apos;m two holes down that impossible shot I need to pull that off right now and when you do the reward is so big you&apos;re like oh my gosh I&apos;m back in it now you know I did I I that that little tiny thread of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:38&lt;/strong&gt; · a needle that I needed to just get it onto that certain section and I did it and now I&apos;m like only one down or I&apos;m one up now it&apos;s just it&apos;s the best feeling but knowing that there&apos;s so many options and when to play those options you know especially when you&apos;re playing multiplayer player it&apos;s like when to hit the gas when not to hit the gas and that&apos;s golf essentially you know that&apos;s that&apos;s that&apos;s what&apos;s going to give one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:04&lt;/strong&gt; · person you know making early plane flights out of the tournament and the other one raising the trophy at the end you know is making those risk reward decisions and there&apos;s tons of them in prop put that&apos;s for sure so are you working with like the built-in physics from the game engine or are you kind of rolling your own or maybe a little bit of hybrid um no this is this is ours you know we really kind of developed this over the course of our first two iOS apps um uh Golf Scope and simply putting&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:33&lt;/strong&gt; · you know and it really was we looked at everything that had kind of preceded Us in terms of digital golf balls and kind of came up to that conclusion that nobody had gotten it right nobody had gotten it where you feel like you&apos;re hitting a ball and it&apos;s rolling like a real ball and all of that and you with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:54&lt;/strong&gt; · that kind of you know hurdled to climb because nobody else had really done it right we kind of just set off and said no we&apos;re going to do it ourselves and it was really important that we used everything we learned through our iOS apps and translated that into VR because especially in VR when it comes to putting it&apos;s so important you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:16&lt;/strong&gt; · know when if you think of it this way when you get up and I think this is why golf simulators work for everything except putting I mean when you get up on a t box on a golf course and you&apos;re on a hole that&apos;s 450 yard long you don&apos;t sit there and have to understand every five feet between you and where you hit your driver at 280 yards okay you know okay&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:42&lt;/strong&gt; · there&apos;s the Fairway I need to hit it straight and it needs to go 280 yards but I don&apos;t need to understand you know how it you know how much wind resistance it&apos;s going to have because it goes past those four trees on the left I mean Bryson D Sho might want to know that but most people aren&apos;t that way and so a TWD&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:05&lt;/strong&gt; · representation of that works perfectly so that&apos;s why a simulator works for playing from T up to the green but in order to truly understand what you need to do to put a ball 20 ft&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:22&lt;/strong&gt; · into a hole you need to get something that is close to feeling all of the elements between you and the hole meaning what that what speed that surface rolls at the undulation for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:37&lt;/strong&gt; · every inch along the way and the only way you can do that is truly in VR you know we do a couple of different things in a prop put one of them being teleport to be able to move you around so literally you can go to the other side of the hole and look at a putt from the other side of the hole before you hit it like you would on a real Golf Course or go halfway down the putt line and go okay this is where it&apos;s going to break this is the feeling I get at this point in time you can&apos;t do that in a 2d realm truly where you get a sensation of what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:08&lt;/strong&gt; · the ball&apos;s going to do when it rolls past that point so that was uh that was very keen in the physics that that it became that reality and and and in VR we think that we really you know were able to nail that or it you can treat it as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:24&lt;/strong&gt; · you need to treat a putt with that feel um Incorporated in putting and giant mode too of course oh of course giant mode yeah so I can imagine building such a physics engine and just getting the feeling right is already a huge challenge but besides just a physics um VR in general is a relatively new field hasn&apos;t been around for that long um have there been any pain points or things you&apos;d say um um were major&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:57&lt;/strong&gt; · pain points When developing for we are specifically the shorter answer of that is no um other than just having to kind of unleash your mind in a different way um especially when you&apos;re you&apos;re in in ux and UI you&apos;re sitting there and so used to dealing with 2D space um that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:22&lt;/strong&gt; · you now have that great opportunity to do things that aren&apos;t possible um you can&apos;t reach into the screen of your computer you know but in VR literally you can reach forward and touch something so I think one of the biggest challenges was just sitting there and and reiterating on that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:44&lt;/strong&gt; · ability and always learning that we have all this space that you can put things you know it&apos;s you&apos;re not like think of an iPhone you know you&apos;ve only got so much screen space that you can you know put UI on there so in VR you have to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:03&lt;/strong&gt; · kind of like relearn yourself and go hold on a second I don&apos;t have what&apos;s just in my field of vision it can be off to the side and back here or up high or on a mountain side or anything like that like you can put something you know 150 yards away that&apos;s up there that will come into people&apos;s view at interesting times or they can turn to it you know so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:31&lt;/strong&gt; · I think that&apos;s one of the the things that that we learned very quickly that you know hey we don&apos;t have to operate in a in a rectangular or square space we can just kind of operate anywhere I think you also get the um benefit of using sound and also haptics which you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:49&lt;/strong&gt; · don&apos;t get on mobile phones or anything like that just like where you can like trigger a noise and just subtle enough to where like the user does doesn&apos;t even have to see it they can just understand what&apos;s going on just by feel like you know I can be putting and just you know hit the trigger the ball can come in I know when my club touches the ground like there&apos;s so many like non visual things that just make the experience that you just you just can&apos;t do with even a controller yeah and I I think especially with the quest with the spatial audio as well you just you even&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:20&lt;/strong&gt; · though you&apos;re looking One Direction there&apos;s so many things happening that just fill in the the the blind spots that just immerses you in it all you know I mean VR is is so immersive onto itself and so 3D but that audio as well&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:38&lt;/strong&gt; · that just kind of fills in the blanks behind you that you know that two seconds ago I turned around and there was a bird flying overhead and I the waves were lapping against the beach there I saw that but when you hear the audio behind you it fills in the blank that it&apos;s still there even though visually it&apos;s not and I think that that&apos;s really really key for for VR and another thing that we we dealt with as well is really you know developing the audio in it that that that made it so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:10&lt;/strong&gt; · engaging even even sounds that you never would have used usually in a in a in a video game on a on a console or something like that that needed to be done so that they really affected you emotionally like the the sound of when the ball goes in the cup is is quite a jolting sound and it it emanates out and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:35&lt;/strong&gt; · it just adds to the satisfaction that that ball went in the hole because it&apos;s such a grand big like just final like you did it sound um so that was really key that things like that had to get right so are you guys developing for multiple headsets too or just targeting a quest at the moment um you know what it&apos;s it&apos;s it&apos;s been all about the quest&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:56&lt;/strong&gt; · right now but uh you know who knows what what happens in the future you know it&apos;s uh we want to reach the biggest audience you know to grow the game of golf like we want to grow the game of golf um you need to reach as many people as possible you know and so if there&apos;s other platforms that uh that make sense for us&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:17&lt;/strong&gt; · yeah absolutely if that gets us to a bigger audience 100% so cuz I guess it wouldn&apos;t be too hard to add devices cuz once you have game in VR I don&apos;t think it would be that hard to add other ones but that&apos;s was kind of more curious if like if you would run into challenges of like okay now we&apos;re doing psvr and you have different types of controllers if everything changes or if a lot of it stays the same yeah I I I mean the physics engine obviously will will stay the same I think the the biggest thing developing for another platform um you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:49&lt;/strong&gt; · know say even PS5 looking down the road you know type of thing is that expectation by a user that you&apos;re using all of the Power of that you know platform so obviously with the quest um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:07&lt;/strong&gt; · the processing power isn&apos;t you know as significant as some of the other devices just because you don&apos;t have supercomputers connected to it or you know yeah consoles or things like that so there there are limitations when it comes comes to developing on the quest in terms of you know processing power and Graphics so I think going to another platform you know the expectation would be that okay you know there might need to be start being some thought about hyper realism um again what we&apos;ve we&apos;ve&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:40&lt;/strong&gt; · developed and I think this was good for us developing on the quest first is if you&apos;re given all the bells and whistles and all the toys that there are out there to develop something I think you a lot of developers tend to lean on on the eye candy rather than thinking about the emotional aspect of it um and that&apos;s not&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:03&lt;/strong&gt; · what we wanted to do we definitely wanted to provide something that had an emotion so the graphics while we&apos;re very proud of them um aren&apos;t the the the main um Gathering point of why we feel somebody will really adopt and hold true&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:23&lt;/strong&gt; · to Heart prop put it&apos;s because the social the emotions the the feelings that you get that that replicate something that you&apos;ve done in the real world in VR yeah and you know I think people may not really realize that when you&apos;re developing VR you have to like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:42&lt;/strong&gt; · think about how you design the environments because you&apos;re not going to just like use like photo realism grab images of golf courses and grass because then all of a sudden it like feels weird and it&apos;s like but somehow low poly feels right and it&apos;s just it&apos;s it&apos;s funny how like it doesn&apos;t seem obvious you&apos;re like well obviously the best thing would be to look at an actual golf course but actually no like in VR it kind of feels weird yeah it&apos;s yeah I think a lot of people have tried um in VR platforms or or devices that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:14&lt;/strong&gt; · aren&apos;t connected to a supercomputer to try to go really realistic in the graphics and it just comes off as incredibly unrealistic and then it just in evades that VR experience because you&apos;re like I&apos;m trying to get something that I think your your brain in VR a lot of times tricks you you know it fills in the blanks and things will happen that your mind will kind of unlock Sensations that aren&apos;t there I&apos;ll never forget one of my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:46&lt;/strong&gt; · after I had my horrible Gran Turismo experience the next thing I did was in psvr again called London Heist and there&apos;s this pointed in London heist where you&apos;re in this Lorry and you&apos;re going down like you know whatever one of those highways are M6 or whatever it is in London and uh like I are coming by on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:06&lt;/strong&gt; · motorcycles and shooting at you and the guy&apos;s like grab the gun out of the console and like start shooting and one of the things you could do was you could open the door and stick your head out of this moving truck and look back and start shooting at guys I&apos;ll never forget&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:23&lt;/strong&gt; · opening that door say my head out and I swore I felt the rush of wind coming across my face and I had to catch myself I was like what and I was talking to some other people and they were like oh yeah I felt the same thing so it&apos;s like you don&apos;t need ultra realistic Graphics&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:42&lt;/strong&gt; · to fill in those blanks of Sensations and I think that&apos;s the mistake a lot of people tend to make in vrs they&apos;re trying to make it so real and it just doesn&apos;t get there yet I think eventually it will but uh no for us it was it was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:59&lt;/strong&gt; · more about filling in the the sensation blanks And the emotions you know and the you know owning a style too you know yeah I also think The Uncanny valy plays a big role in this because um if you try to go too realistic but it&apos;s not completely There Yet the brain will just spot the mistakes and just going to feel weird but with low poly it obviously&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:23&lt;/strong&gt; · doesn&apos;t look like the real world but the brain is able to understand it and therefore translate it and attach all the feelings you would have with a real object to it yeah yeah almost like the feeling like you know your avatar in VR becomes your avatar in virtual reality and it&apos;s just there&apos;s that feeling that like I still catch myself like months&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:43&lt;/strong&gt; · and months and months like almost a year after I&apos;ve had my headset like every time I&apos;m on a cliff like I still catch myself like feeling like I&apos;m about to fall even though like I know for a fact like the ground is right there like happened in you prop put and a couple other games like it just your brain just can just slip out of reality so easily um but like you said if the game is not built correctly like it&apos;ll break you out of that immersion pretty quickly you folks obviously have a built up quite a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:11&lt;/strong&gt; · bit of expertise and experience building VR games comes to sound physics graphics and just the overall development what advice would you give to somebody who&apos;s totally new to VR and like to start to build something is there something you could say that&apos;s the one advice I&apos;d give to somebody new I think to to pick one of two paths and that would be a to try to replicate&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:42&lt;/strong&gt; · it&apos;s this is this is going to be a a funny thing funny exercise to do but to build into VR is whatever you&apos;re wanting to build okay and let&apos;s say that&apos;s a sale saing app okay so I want to build the best sailing app out there and I would to do that in VR my first thing that I would suggest you do is close your eyes and start recognizing everything that you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:13&lt;/strong&gt; · feel in sailing rather than what you see in sailing like that&apos;s where you start so if you&apos;re trying to replicate something real close your eyes and then start developing from that sense of taking away the visual aspect so the sounds the smells you know even though you&apos;re not going to smell something there your your nose your old factory will fill itself in in VR so develop for all the senses minus visual first and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:46&lt;/strong&gt; · then start adding the visual on top of that so that would be from from my advice in a real sense the second one would be do not limit your mind as to where you could take somebody truly so I mean you can take anybody anywhere take them&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:09&lt;/strong&gt; · inside your mind and develop from there you know we all can have dreams and we all see things when we close our eyes or we Vision things that that aren&apos;t reality you can make them reality and you can let people&apos;s as as much as you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:27&lt;/strong&gt; · think about them and your skin tingles or the hairs on the back of your neck rise when you think of them you can do that to other people you know you can make them see what&apos;s inside your mind so never limit it and bring people in so I think there&apos;s two paths that you could take in VR that that definitely are going to bring a lot of enjoyment and entertainment to people that&apos;s for sure seems counterintuitive to not develop for vision but yeah that totally makes sense um so how is it developing for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:57&lt;/strong&gt; · people that haven&apos;t used VR before like you know I know you talked about maybe having like a a go top golf Lounge where they can try it out but you know some of these people are going to be the first time ever touching a VR headset but then you have other people that you&apos;re targeting Gamers who do know how to use VR so what&apos;s the experience with that where you&apos;re trying to create controls and experiences that are unique like that I think that&apos;s one of the the the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:23&lt;/strong&gt; · difficult things when you know it&apos;s it&apos;s like the let me take you back to the Wii okay is the beauty of the Wii is if you were bowling you looked at somebody you said that never that doesn&apos;t play video games they go well what button do I push and you went you don&apos;t swing it like you would a bowling ball and then somebody would go oh okay and then they could play like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:49&lt;/strong&gt; · you you put something like a fortnite or something into somebody&apos;s hands and it&apos;s just a bunch of of buttons and somebody goes I have no idea what to do and it becomes so daunting and it becomes such a learning curve and such an effort to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:05&lt;/strong&gt; · even play let alone compete online you know and I mean that&apos;s I&apos;m not a big multiplayer online person when it comes to things like fortnite or Call of Duty or stuff like that because I&apos;m just tired of getting like head shot by some like 8-year-old like the second I respawn it&apos;s like okay I&apos;m laying on the ground the whole time this isn&apos;t Fun you know and I recognize that I need to put so much in so that I can last even 4&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:32&lt;/strong&gt; · seconds longer than I did for the last month you know and then that&apos;s going to take me a month to get to the next eight seconds and you just go it&apos;s too daunting um so I think one of the the difficult things with with the quest A lot of times can be and you know just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:50&lt;/strong&gt; · seeing people for the first time holding it because first off you&apos;re in are so your face is covered so when you say to somebody pull the grip trigger you know which is this trigger on the side of the controller you almost have to grab their hand and go this finger this button and push it in for them and they go oh and it starts taking a bit of muscle memory to understand it now the majority of the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:15&lt;/strong&gt; · the demographic of a quest user right now is a gamer so they know what a controller is and they know how to learn button positions and things like that quite quickly but if you&apos;re going to get to a mass audience you need to limit the amount of buttons now the quest has done it quite well and it becomes quite intuitive and their onboarding when you first put on the quest is is quite good but Translating that from different game to different game is tough the beauty of prop put is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:45&lt;/strong&gt; · once you kind of activate yourself it&apos;s basically the trigger is going to activate the ball or you Orient yourself is the trigger is going to activate the ball you putt you pull the trigger to follow it or go to your next putt or anything so there&apos;s not a lot of buttons that are there it becomes very much like the Wii in the sense of like do you know how to hit a putt yeah do that motion&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:11&lt;/strong&gt; · and they hit it and the ball goes like oh I hit it and you&apos;re like yeah and that&apos;s it so I think that&apos;s that&apos;s one very special thing about prop put that&apos;ll allow um new people into VR to Gras grasp a game that they can you know pick up very quickly and go okay I don&apos;t need to learn you know all these button Combos and stuff like that in order to play this thing you know so taking this even one step further have you um to uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:42&lt;/strong&gt; · have you looked at hand tracking Oculus is now starting to support like not having any buttons in the game so uh yeah it&apos;s it&apos;s something we&apos;ve kind of played with I think um um more from a side of like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:03&lt;/strong&gt; · novelty um type of thing rather than than anything else um it&apos;s it&apos;s new for them as well so I think if it came down to us doing anything with it I think one important thing even though the quest controller doesn&apos;t weigh a lot The Sensation that there&apos;s some bit of weight in your hand when you putt is important so I don&apos;t ever see us like getting rid of the controller completely and like just putting with your hands together um I I think for super golf&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:36&lt;/strong&gt; · enthusiasts you&apos;ll see them doing that in the middle of an aisle at Home Depot but it probably doesn&apos;t translate too well into into a VR game so the any bit of weight is good I think where would come into is being able to do any sort of um you know menu you um you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:56&lt;/strong&gt; · access or option selections or things like that um that we would you know possibly look at it there but who knows you know our our minds are Unleashed so it could be something that we we discover that that people enjoy doing that you know I mean eventually maybe it&apos;s one of those things to get ultra realistic you know most Pros when they go to hit a putt they read the Putt and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:22&lt;/strong&gt; · the last thing they do is they have a whether it&apos;s a line or it&apos;s the Titleist logo or something like that on the ball they take that and they line that logo up exactly on the line that they&apos;re going to put so when they get over the ball and they look down they don&apos;t have to rethink it it&apos;s like okay I&apos;m lined up I need to like align my feet my shoulders and I need to stroke it exactly on that line so I could see us where you know maybe in the future you actually took the golf ball in your hand&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:52&lt;/strong&gt; · with the hand tracking and kind of like put it down and like manipulated so the line the logo was exactly where it needed to be and then you could get over the ball and hit it so things like that I think could be interesting in the future where hand tracking would come into play yeah I actually do that one quite a lot when I&apos;m playing physical Putt and you know like you also kind of like sit on your Club so you kind of look down the line and yeah there&apos;s a lot of things that you don&apos;t realize you do the great things about releasing um your your game Into the Wild is uh is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:25&lt;/strong&gt; · people are going to start finding out what they like they&apos;re going to start telling others about what they like they&apos;re going to start doing it a lot more and we can we can look at look at all those things and listen to all those comments and start developing it even further um I think a perfect example of that need is what&apos;s happened um currently in Sim racing um you know before before&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:52&lt;/strong&gt; · everything that that hit us as a Society hit us I mean you very few people in this world that are di hard NASCAR fans or especially Formula 1 fans would ever sit there and go yeah I&apos;m going to spend a Saturday afternoon watching Sim racing and you know what now hundreds of millions of people do that each week and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:18&lt;/strong&gt; · religiously and what they&apos; they&apos;ve seen is that the reason why they&apos;re watching it and believing in the realism is because you get a a driver like Charles ler Orlando Norris or George Russell or these guys that are on their simulators and that wheel is about as realistic as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:41&lt;/strong&gt; · it gets and it&apos;s physical and if you hit a a burm the wrong way it&apos;s going to twist your wrists off and then you realize that that takes all of that talent to to manipulate that wheel and go through the gears and all of that rather than just using a joystick and the realism comes into play for everybody so I think there are going to be that opportunities going forward in any sports game in the digital sense that that link into something in reality&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:15&lt;/strong&gt; · that even UPS the Precision that you need to have to compete at a level then I think that&apos;ll be important you know and uh we&apos;ll definitely be in position for making that move at the right&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:31&lt;/strong&gt; · time how has the beta program been so far when you know I think you guys have Discord and some other ones like how&apos;s that been working with people that like you said maybe golfers maybe Gamers maybe yeah I mean I have to give a major shout out to to our beter testers they were just truly an incredible impressive bunch of people people from everywhere all over the world I mean they they really gave us&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:02&lt;/strong&gt; · insight into um things that we weren&apos;t looking at um they really praised us on ideas that we maybe jumped a little far ahead and were wondering how those would come out um I mean they they played it senseless I we&apos;ve got this this one guy down in Naples for I it is truly amazing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:26&lt;/strong&gt; · the amount of prop put he played through beta it was and the feedback that he gave us and just stuff that we really like took to heart and and you know Incorporated in the final product but it was a they they were really really special to to give us that amount of time to to shape this and and they are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:49&lt;/strong&gt; · they are definitely the the 12th man on the team um for sure when it comes to what you&apos;ll see in the in the Oculus store in a couple days so um prop also supports a multiplayer mode where you can get paired up with our players uh which are a similar skill level um how are you dealing with the well-known chicken and act problem where nobody can get paired up with somebody if there&apos;s no players but nobody is trying if there&apos;s uh nobody who can get pair up with yeah it&apos;s well right now when&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:22&lt;/strong&gt; · you&apos;ve got a small beta group it&apos;s a it definitely is challenging so it&apos;s it&apos;s kind of you know a range of time which has been awesome because we&apos;re like okay we&apos;re going to try this at this hour and you know people show up and they&apos;re ready to go and they&apos;re ready to go like past what my stamina is so it&apos;s like wow&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:40&lt;/strong&gt; · like they played for that long like that&apos;s awesome um you know when it&apos;s when it&apos;s live in the store you know it&apos;s G to it&apos;s going to come down to how fast people are downloading and getting to that point of of wanting to play multi player I mean one thing I want to stress is it&apos;s it&apos;s a social experience unlike anything out there for sure I mean it&apos;s just it is really it&apos;s so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:06&lt;/strong&gt; · personal it&apos;s so social it&apos;s so emotional that it&apos;s like man i&apos; I&apos;ve played a lot of multiplayer experiences both in VR and in you know other digital Realms and it&apos;s I&apos;m not you know patting us on the back that much or really trying to promote our product on beyond the truth and it really is something special when it comes to um playing you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:30&lt;/strong&gt; · know multiplayer and prop put but uh if there&apos;s not somebody else to play with there&apos;s always the bot um so you have multiple levels that you you know he&apos;ll difficulty that he&apos;ll play at or she&apos;ll play at um and uh I&apos;ll tell you what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:46&lt;/strong&gt; · there&apos;s some times where you&apos;re like and what&apos;s I think really good about our bot as well it never feels like there&apos;s programmed responses like if I choose easy it&apos;s not like okay on that hole he always plays that hole the same no it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:03&lt;/strong&gt; · not I mean I&apos;ve seen on easy on some holes where you&apos;re like oh there&apos;s no like I can&apos;t make this on easy he&apos;s not gonna make it and somehow another he makes it you&apos;re like whoa and it also doesn&apos;t feel you know FIFA went through I think FIFA and Madden both went through a phase before AI really was you know what we know AI is and they would affect some things that would change the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:31&lt;/strong&gt; · kind of flow of the game so if all of a sudden I was up by a ton in Madden or FIFA all of a sudden it was like like in FIFA I couldn&apos;t tackle somebody or they were running faster than me or they was like where do they get this superpower like 78 minutes into the match it&apos;s like come on this is so unrealistic it was just to keep it close and and interesting um with the bot and prop putt you never feel that you never feel like okay well now he&apos;s got superpowers&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:59&lt;/strong&gt; · and he&apos;s coming back it&apos;s it feels you know many people will say this about a bot but it feels like it has personality and it feels like it can make decisions um and that anything is possible with it so uh you know I&apos;ve seen it take some routes that you&apos;re like really like yeah that one you know and I&apos;ve seen other things where it&apos;s like okay I&apos;ve got this guy all of a sudden it&apos;s like and it&apos;s I think what it is is not so much the bot is playing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:30&lt;/strong&gt; · better at that point in time but you&apos;re starting to beat yourself just like in a real golf you know you&apos;re you get your confidence is high you&apos;re like a I can hit every shot and you know your buddy is like um you can&apos;t make that 220 yard green with a nine nineiron oh I could do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:48&lt;/strong&gt; · anything today you know what it&apos;s in the water every time you know so I think what happens more often is that your confidence gets high or your nerves get to you and you start making the mistakes it&apos;s not that the bot is actually getting better so uh so yeah it&apos;s uh We&apos;ve solved that if you if you want to get out there and play with somebody that that that dude&apos;s always ready for for for a match and you put him on a hard or Pro and you could really be in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:17&lt;/strong&gt; · for some misery you got to be on your on your game every shot and so it&apos;s it&apos;s fun well I think like you said I think something you guys nailed was the multiplayer aspect and for me I I had such a good time playing the game because there&apos;s something about like you know a lot of these rooms like big screen and VR chat and ALT space where&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:42&lt;/strong&gt; · you&apos;re just sitting around talking but there&apos;s something about like talking while you&apos;re doing an activity that I feel like really lowers the threshold for a lot of people that maybe are socially awkward or have a hard time um talking with people whereas when you&apos;re doing an activity you really start to open up because you&apos;re having to focus on other things and um I think that part&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:02&lt;/strong&gt; · is super cool and then so with that you know it&apos;s a double-edged sword how do you guys deal with you know spam and abuse like so let&apos;s say you do have a a person that is a bad egg that is you know harassing other people yeah I think uh it&apos;s it&apos;s one of those things that I mean obviously we haven&apos;t had it yet um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:22&lt;/strong&gt; · so I think it&apos;ll be a little bit that we we&apos;ll have to deal with it as it comes I think I think those people exist in the community kind of ostracizes them pretty quickly and they realize they&apos;re not getting to anybody and then they&apos;ll go find another game that they can do it in um there&apos;s nothing really in prop put that that really promotes somebody being&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:51&lt;/strong&gt; · that way you know where it&apos;s like you know it just you get in there and you feel like okay we&apos;re all here to have a good time and compete and stuff like that now if we start doing tournaments people are going to get serious you know um you can always mute somebody you know so you don&apos;t have to hear them if they&apos;re they&apos;re talking a certain way I mean we&apos;ve all played with a golfer that just has a horrible temper you know and it&apos;s it&apos;s kind of funny how expressive the putter can be in uh in prop putt so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:21&lt;/strong&gt; · you can get a sense of that what a bad day somebody&apos;s Happ by the way that they&apos;re waving the putter or smashing it into the ground when they miss a two-footer um but as as far as the abuse side of it you know if if if somebody&apos;s obviously getting out of control then there&apos;s there are different ways that we can deal with that so um I think VR is a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:43&lt;/strong&gt; · pretty good place to uh place a few Easter eggs here and there uh does prop put have uh some hidden Easter eggs to explore and to find well Norbert I&apos;m going to answer that question quite easily and quite complexly at the same time yes nice they are there um there&apos;s some yeah there&apos;s some great things always if I can give you this one word of advice is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:12&lt;/strong&gt; · giant mode is very keen not only for seeing where you need to go on a hole um something you know it comes in very early on a hole three on the beach course you know it&apos;s your third hole into your campaign where uh it&apos;s a par four and you need to get up in the air and really see where you want to leave your t- shot um but going into giant mode can also put you in places where there might be Easter eggs um and then you know just tap your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:45&lt;/strong&gt; · putter on a lot of things you know everything is is engaging you know and interactive in it so just go around just beating things with your Putter and uh ni that&apos;s good advice some some point in time something might you know pop out at you nice nice well Jason we&apos; just like to thank you so much for being on our podcast today so where can people find&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:09&lt;/strong&gt; · um information about prop put and follow you on social media yeah so we&apos;re on you know all the social media platforms on Discord um you can go to prop.com um that&apos;ll show you the trailer and a bunch of the features and you know take you all the special places at the same time and then uh you know it uh you&apos;ve&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:33&lt;/strong&gt; · got an A Quest head into the store uh today you can see some screenshots and read some descriptions and watch the trailer and things like that in a couple of days you know either jump into your Oculus app or jump in your headset and click on Prop put and download it and and meet us out on the course you know if you&apos;re looking for a real challenge you know go on YouTube um I&apos;ll kind of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:59&lt;/strong&gt; · reveal to everybody um there&apos;s this character on YouTube doing prop putt videos his name&apos;s smooth ketchup you&apos;ll see him at the top of the leaderboard a lot of times as well that&apos;s me so bring it on yeah I&apos;m ready for a challenge just uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:16&lt;/strong&gt; · smooth catch up in in in in in friends and invite me and uh we&apos;ll go head to head you know but uh nice but smooth smooth will R you when you&apos;re down I&apos;m Gonna Knock You down even further so uh be prepared for the banter that&apos;s all I got to say so really you&apos;re saying that uh by building the game you&apos;re just finding a way to compete with everyone in the world so absolutely that&apos;s that&apos;s it you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:48&lt;/strong&gt; · that&apos;s well uh make sure to you know look in the description and show notes for for all the links that we link well we&apos;ve been talking about um thank you so much again Jason for being on and um we hope to see you in the next episode yeah thanks a lot guys really had good time [Music] today&lt;/p&gt;
</content:encoded></item><item><title>Flutter Testing and AppStore Rejection</title><link>https://rodydavis.com/podcast/creative-engineering/flutter-testing-and-appstore-rejection/</link><guid isPermaLink="true">https://rodydavis.com/podcast/creative-engineering/flutter-testing-and-appstore-rejection/</guid><description>This discussion explores comprehensive best practices for Flutter testing, covering unit tests, mocking, state management, code coverage, crash logging, and navigating AppStore rejection hurdles.</description><pubDate>Tue, 07 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Flutter Testing and AppStore Rejection&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;LAcE2YfQT2Q&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Follow Up&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rody experiences with apple approval&lt;/li&gt;
&lt;li&gt;Rejections&lt;/li&gt;
&lt;li&gt;Recourse&lt;/li&gt;
&lt;li&gt;Options&lt;/li&gt;
&lt;li&gt;2 million people using flutter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Testing&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;State management and testing trade offs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UI logic and replaying capabilities&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Logging&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mocking&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Smoke Tests&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MVVM&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Firebase&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Filesystem&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Folder Structure / Layers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Packages that can be tested&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Code Coverage
&amp;quot;flutter test --coverage&amp;quot;&lt;/p&gt;
&lt;p&gt;https://codemagic.io/start/
https://sentry.io/welcome/&lt;br&gt;
https://pub.dev/packages/mock_cloud_firestore
To visually run widget tests: flutter run test/widget_test.dart
Suggested finders: Just tap anywhere when running a widget test using flutter run&lt;br&gt;
https://pub.dev/packages/device_preview&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Logging&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sentry&lt;/li&gt;
&lt;li&gt;Crashlytics&lt;/li&gt;
&lt;li&gt;Flutter Testing&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;li&gt;Flutter Driver&lt;/li&gt;
&lt;li&gt;Unit Tests&lt;/li&gt;
&lt;li&gt;Flutter Octopus&lt;/li&gt;
&lt;li&gt;Flutter Interact&lt;/li&gt;
&lt;li&gt;Flutter VR Testing&lt;/li&gt;
&lt;li&gt;Xcode testing, Android testing&lt;/li&gt;
&lt;li&gt;Flutter i18n Localization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Norbert Kozsir - @norbertkozsir&lt;/p&gt;
&lt;p&gt;https://twitter.com/norbertkozsir&lt;/p&gt;
&lt;p&gt;https://github.com/norbert515&lt;/p&gt;
&lt;p&gt;Rody Davis - @rodydavis&lt;/p&gt;
&lt;p&gt;https://twitter.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://github.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://youtube.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://rodydavis.com&lt;/p&gt;
&lt;p&gt;Our podcast player:&lt;/p&gt;
&lt;p&gt;https://rodydavis.github.io/creative_engineering/&lt;/p&gt;
&lt;p&gt;Follow on Twitter:&lt;/p&gt;
&lt;p&gt;https://twitter.com/CreativeEngShow&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · welcome to creative engineering with me I have my co-host sbert today we&apos;re going to be talking all about testing so how have you been recently I&apos;ve been great I don&apos;t know how it&apos;s um for you but quarantine time flies like I don&apos;t know like since the last episode it just failed like was a few hours ago um how&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:18&lt;/strong&gt; · it been for you yeah I uh I definitely have the same experience it feels like you know every day is a Saturday so it&apos;s like you Monday starts and then oh it&apos;s Saturday again and then it feels like uh I feel like I&apos;m getting a lot done but at the same time it just you know if you&apos;re not careful it just you uh you really start to yeah time really flies that is true um in one of your recent tweets I&apos;ve seen you had some problems with apple and one of your test Labs um do you want to share your story yeah so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:48&lt;/strong&gt; · this has been um something that&apos;s been pretty difficult like over the course of like more than a year I created this uh flutter application that allows you to to control real Tesla vehicles and it was super cool I uh I&apos;ve been using it every day for over a year been constantly improving it um even worked with my sister to be able to do the animations for it she created it all in RVE and you know hooked up all the um different nodes to be able to like animate any part of the car so yeah we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · we&apos;ve sunk a lot of time into this but yeah so I got the result from Apple saying yeah we&apos;re not going to move forward with it we&apos;re going to reject all these updates because we feel that it is in violation of our policies and so by appealing multiple times um I keep&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · getting the same person and multiple times they keep saying um that they have changed their guidelines to where they&apos;re not allowing people to release applications that control Hardware that is not made for the manufacturer which was crazy and I was like well maybe they&apos;re just meaning cars but when I asked them they gave me the example of well a developer cannot create an app that can control a Samsung TV and them not being Samsung and I said that was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:10&lt;/strong&gt; · kind of crazy and there&apos;s a ton of apps that are doing this like are existing applications going to be able to get updates and they said uh they weren&apos;t able to comment about it and so that was really frustrating um as well as you know there&apos;s many other third party Tesla apps on the App Store um and my goal with mine was to make one that um was focused on UI and user experience because I felt like a lot of them had some stuff I could improve um but yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:37&lt;/strong&gt; · uh they have said you know we&apos;re not accepting any new applications that will control hardware and the only way that I can get my app approved is if uh Tesla explicitly gives me a letter saying that I can exist on the app store which of course this is a owner API meaning that you have to own a Tesla car to be able to use this API anybody with a Tesla app&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:01&lt;/strong&gt; · um has to have access and log in with credentials so yeah um one thing that could help is if Tesla has an official ooth you know guidelines but based on the post I made on Reddit I feel like there may be something bigger going on especially since you use your phone app as a phone key Tesla may be working with apple directly to uh take down some third party applications or at least prevent new ones so that&apos;s uh something&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:28&lt;/strong&gt; · pretty interesting um people have definitely been bringing up some areas that I wasn&apos;t considering for uh before but you know it&apos;s uh also just part of this process has just kind of taught me about like how to deal with rejections on the App Store because for me I&apos;m am thankful that I have a ton of users on Android even though I don&apos;t personally use it on Android and then I also have a lot of people on the test flight um but it really things like this really pushed me towards U pwas all the time I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:01&lt;/strong&gt; · absolutely love the App Store be able to do a lot of things on there um you get to do a lot of you know offline access and of course you can do this with PDA pwas but there&apos;s areas in which Native applications can just kind of get more raw performance out but at the same time&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:18&lt;/strong&gt; · one nice thing about the web is that it&apos;s open and then that anybody can release an application you know there&apos;s no gatekeeper um yeah and it just has really started to pushed me towards you know making apps for the web first and then for the App Store second um one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:37&lt;/strong&gt; · caveat with this is you know desktop you can release outside the app stores which is really cool and I think that&apos;s an awesome feature of it but yeah I just curious your thoughts on you know gatekeeping and if you&apos;ve had similar experience with Google Play yeah like it really sucks that um you can&apos;t do too&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:56&lt;/strong&gt; · much because the App Store is the thing people are using like are there are options to distribute apps on iOS or is it exclusively App Store yeah so when it comes to you know releasing other applications the only real thing that you can do is um you know have like a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:15&lt;/strong&gt; · Enterprise profile which they&apos;ve been really um you know cutting that down recently because if you release an app uh with a provision profile for example on the App Store the only way that they can install it is if they go and install the profile directly and then they get access to the app via a link or whatever but um I know there was like a class action lawsuit against them recently about the fact that you couldn&apos;t install third party apps Sid loaded so yeah it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:44&lt;/strong&gt; · it&apos;s pretty interesting um one of the reasons why the app store is so protecting of this is because you know they want their their cut of all the sales so if you&apos;re on the app store for example you have to abide by the guidelines which means that you give like 30% of your inapp purchases you give 30% of your subscription cost and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:06&lt;/strong&gt; · yeah you&apos;re not allowed I don&apos;t know people know this but when you when you have an inet purchase you&apos;re not allowed to use stripe or PayPal you have to use the app store um purchase for this same for Google Play but because of this it means that um they get a cut of everything which is fine you know they&apos;re hosting the App Store and all this other stuff but it still kind of stinks that there&apos;s no third party option because sometimes there&apos;s just applic that exist in the world that you know I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:33&lt;/strong&gt; · get it there&apos;s going to be malicious apps out there always apps can be abused but there&apos;s always going to be apps out there that don&apos;t really fit the mold and you know are community-driven apps like for example like what if you&apos;re your favorite or I had a Honda Civic at one time so one thing that was crazy is like after you know it was like a 2014 or something and then when I got the car&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:57&lt;/strong&gt; · you know the next version supported carplay that same model could have supported carplay it has the same Hardware but they chose not to update it it&apos;s not like a Tesla I can&apos;t just get in over the a update and in those situations I feel like after a certain amount of time they should allow like open it up to let you do anything um you know like a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:17&lt;/strong&gt; · developer should be able to just install stuff on it U when it&apos;s unsupported for example like on Mac OS and iOS I think it would be really cool is like when Apple UNS supports an iPad or an iPhone I think be cool if then they they remove the Restriction of only installing app store apps because think about how many apps you download on the App Store like on iOS on an unsupported device wouldn&apos;t work almost all of them require the latest so being able to like extend the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:44&lt;/strong&gt; · life to be you know okay well we can just install any application and of course you know you can always install stuff directly from xcode but I just think that there&apos;s a bigger market for this kind of area of applications that live out outside the life um of the device yes yeah talking about the um not&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:05&lt;/strong&gt; · being allowed to use stripe nor payment platform wasn&apos;t there a big thing with Spotify I think where they were using some sort of third party um payment service and apple really didn&apos;t like it or yeah so well Spotify yeah they had a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:22&lt;/strong&gt; · really big problem with that it&apos;s um specifically their guidelines are about offering a subscription that goes outside the App Store you&apos;re not allowed to link to that subscription website you have to offer that subscription um via a link uh via the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:37&lt;/strong&gt; · appstore purchases inside the application which really sucks but what&apos;s interesting is for on demand video services like Amazon they recently changed the rules that lets you if you have a subscription like Amazon Prime they&apos;re allowing people to sign up for that subscription um inside the application which is kind of crazy that&apos;s never been done before this is also the same reason why Amazon Kindle you can&apos;t buy books on the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:03&lt;/strong&gt; · IOS app you have to go to Amazon website which it doesn&apos;t link you to and you just have to kind of like figure it out that if I buy it on the website then it&apos;ll work on the mobile which this is interesting this doesn&apos;t apply to PW so I can have a pwa do whatever I want on the App Store and install it to my home screen but the second that it&apos;s inside the App Store um I have to abide by all these rules so I&apos;m very unsure about this but wasn&apos;t there some problems with pwa on iOS too like that iOS doesn&apos;t&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:33&lt;/strong&gt; · support or will not support pwa as much as they should or am I Am I Wrong well it&apos;s kind of funny because at the beginning of the App Store Steve said that all you need is pwa um you know all he said that everyone would just need websites and that&apos;d be enough this is before the App Store existed so to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:53&lt;/strong&gt; · they&apos;ve always had this approach but then when the App Store launched they kind of started changing their mind but I would say that I has pretty good pwa support you can do all your offline stuff your service workers and um I&apos;ve had pretty good experiences with it um there are you know obviously edge cases but um it may not have the latest features as Chrome but I know that they they do support a ton of pwa features in fact I filed a radar once and about a pwa not launching correctly with ooth&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:23&lt;/strong&gt; · and they had that bug fixed within you know a day or two so I definitely know there&apos;s a lot of to development on PW and Safari interesting good to know so um have you heard Flur just passed 2 million people developing with Flur which is big news is that is crazy that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:44&lt;/strong&gt; · is so many people that is bigger than most countries which is nuts yeah I uh and also I think did you see there was like 50,000 flutter applications on Google Play yes like 50,000 like that that&apos;s a ton yeah that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:01&lt;/strong&gt; · would be cool to have some sort of section in the Google Play Store made with Flor or something like that that would be cool that would be really cool um yeah and also I think it&apos; just be cool to have a a conference with 2 million people that&apos;d be awesome as well I wonder how much of that is China&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:19&lt;/strong&gt; · though I don&apos;t heard too much about uh FL in China but I think the community there is actually pretty big well you have people like alib Baba and I&apos;m if I&apos;m not mistaken they have like over 150,000 employees and stuff like that but I know that they use flutter for their applications and yeah I think I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:36&lt;/strong&gt; · think China is at least maybe a third of that I don&apos;t know I could be wrong could be could be yeah and then also an interesting fact about um the survey was that 80% of the people are using Windows that was a shock to me yes like I was of course people are using Windows yeah yeah yeah it&apos;s um it&apos;s pretty&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:58&lt;/strong&gt; · interesting though um crazy to see how flutter is evolving too I mean these were definitely not the same surveys that I remember uh when they came out I think when flutter was first starting Mac OS definitely had a a bigger share of those people using it oh didn&apos;t know about that by yeah maybe but yeah I think like the mass of the people is using Windows because Windows is just working on so many different device like for for a Mac you have to have the one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:26&lt;/strong&gt; · from Apple well the only thing that reason why I thought it was more mac users would be because um Mac OS and iOS both require a Mac and that&apos;s two of their six platforms so I thought that maybe there would be a lot more um but yeah I guess uh the thing to counteract that is there&apos;s a lot more people using Code magic now to be able to release their applications and a lot of people don&apos;t need a Mac when they&apos;re using flutter that&apos;s true that&apos;s true talking&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:54&lt;/strong&gt; · about code magic let&apos;s get to our next topic which is testing um okay so first off um testing State Management and the tradeoffs uh regarding the stability you what are your opinions yeah so I think when people are first starting with flutter applications they may not re think about&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:16&lt;/strong&gt; · testing in mind when they&apos;re creating these applications um one of the things I always like to do is you know it&apos;s not always about choosing the quickest solution it&apos;s about choosing the solution that can be tested scalable and that other people can come in after me and be able to work on this code um there&apos;s a thing called test driven development where we develop code you know testing and then kind of iterate upon it but um yeah I think there are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:43&lt;/strong&gt; · many State Management Solutions out there and they all have their tradeoffs and their advantages for testing for example um not many people know this but if you use Redux you can time travel so when you&apos;re testing you can actually replay all the events that the the user is made up to that point which I think is really cool uh block has its own testing Library which is really nice so you can mock different um data as well&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:08&lt;/strong&gt; · as you know you have um provider which can if you you can you know mock out all the the the trees and stuff like that but you know when it comes down to writing your actual code I think the biggest thing is you know for me it&apos;s folder structure so if I have my you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:25&lt;/strong&gt; · know my data layer and my different layers on this side of thec I&apos;m have my UI folder I know that I&apos;m going to be writing widget tests for the UI folder and then unit tests and everything else for the other folders and layers because you know if if you&apos;re not careful you can kind of start to put a lot of your your logic inside of your widgets and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:47&lt;/strong&gt; · while that&apos;s fine and easy to do it can be really hard to kind of break that out and start testing individual core functions to make sure that these functions are working correctly and as expected yes talking about full structure so I see like mostly two camps of people the one Camp who&apos;s um um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:05&lt;/strong&gt; · separating code by whether it&apos;s UI where it&apos;s logic where it&apos;s models and there&apos;s the people who separate code by the feature like if the code belongs to the same feature it&apos;s in the same folder what do you do yeah I I do I have a UI&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:22&lt;/strong&gt; · folder which with different screens and then subfolders I mean and then I have a a source or a folder which in each one of those I have a classes models I have blocks and I try to separate it out by um feature for sure because like I have like an authentication block I have an editor block I have a settings block and each one of them are scoped to a specific thing but at the same time they&apos;re not so specific to a specific screen um I have some seen people use um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:55&lt;/strong&gt; · blocks or different things to have one per screen which is also pretty cool um I guess it just really depends on yeah how you structure it but as long as you have a standard and you just stick to it um consistency is really important yeah I&apos;m doing something um similar so basically if something is one feature which is really scope to the feature I put everything in um one4 like the block&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:16&lt;/strong&gt; · the the services the UI the screens um having a consistent structure between those features too but if something&apos;s like uh globally available for example configuration off or something I also to put them some sort of a more globally placed folder yes and also when it comes to these tradeoffs too I think you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:39&lt;/strong&gt; · it&apos;s not just about testing too it&apos;s all about logging and analytics um as well as mocking so what kind of stuff do you use to like set up your loging because I know there&apos;s a bunch of different packages out there but I was curious what your approach has been um okay that&apos;s pretty interesting because I&apos;m still trying to figure out um what the best way of approaching this is like what I&apos;ve seen and what I&apos;ve done myself um in the past two is um with the blog package you have all those events and you can register a block delegate which&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:10&lt;/strong&gt; · um listens to all of those events so basically all the event switches which the user is producing which is floting for your application you can just have a delegate which listens to all of them and does logging with that my main problem with this approach is you end up with mostly one huge class which there&apos;s a lot of logging lot of if else&apos;s and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:32&lt;/strong&gt; · also um sometimes you have events which you just use for internal purposes and you don&apos;t actually want to flow through your whole login Pipeline and I think right now I&apos;m more I&apos;m going back to having the analytics in live in the block like of course not in the UI but just having it in the um block whenever some sort of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:57&lt;/strong&gt; · event is coming in or a method is called what are you doing yeah for me I like to use a couple different tools so there&apos;s Lumberjack I believe it&apos;s called Uh logger and um this other uh package but the biggest thing for me is scoping out different type of log events so I have a warning an error an exception uh info um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:20&lt;/strong&gt; · just a regular debug level and that way I can filter out my logs based on each one of those things um in the past I&apos;ve even gone as far as to create you know a sqlite database that then stores these logs for the user so then I can even filter it based on the user ID so every time I do a new log event I just add it to a row in a table and then that way&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:44&lt;/strong&gt; · when a user clicks on a button like send logs like if they&apos;re sending feedback I can actually just grab those that data set from a query and then send it up interesting but what&apos;s interesting is um yeah you can also use use tools out there like Sentry to where if you wrap it you can then um run a Zone appap have you ever run a Zone appap in flutter yes I&apos;m using Sentry all the time it&apos;s uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:09&lt;/strong&gt; · but Zone app is really cool because it&apos;s just part of Dart itself and it allows you to like anytime there&apos;s an exception you can actually you know intercept that as well as there&apos;s a flutter on airor so you can even customize your own error screens for flutter that&apos;s true yeah using it in most of my apps right now um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:26&lt;/strong&gt; · just takes off all the exception handling which like you you would be losing those exception you would be having no idea what happens in your app and it&apos;s a great way to find out um weird things that are happening and you may not see in development yes uh for me before we had&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:45&lt;/strong&gt; · desktop my biggest you know bane of my existence was every time I ran my application on iPhone 5 it was like my application would just come to a halt because it was like the UI was so small that or is I think it was the iPhone 4 the iPhone 5 had worked but the iPhone 4 in landscape with the keyboard up was the worst that I ever had to deal with I think it was less than an inch from the top of the keyboard to the top of the screen wow yeah and I mean and then you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:15&lt;/strong&gt; · have like you know when you have an app bar there like there&apos;s no room for Content so it&apos;s like you know uh I had many uh people complain like project managers and stuff like oh well the boss is using the iPhone before so we have to make it work and it&apos;s like man it&apos;s just like so my solution in those were usually to disable um orientation so it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:36&lt;/strong&gt; · almost always be just a portrait app so that&apos;s what I see a lot of people are doing yeah it&apos;s it&apos;s pretty insane and then I don&apos;t know um if you&apos;ve had the same similar experience on Android but for me I this is why I always wondered why you know on games and stuff and Landscape on the iPod Touch they would always show just an alert dialogue with a text field and it&apos;s because they&apos;re it&apos;s the smallest thing that you could use to that could be moved around on the screen which is funny interesting yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:05&lt;/strong&gt; · um so so we talked about logging but what about when it comes to mocking um because I know that there&apos;s some cool packages I&apos;ve used like you can mock the HTTP package so then you can you know as long as you uh follow the example you can then like you know intercept and say when I do a post request at this point I want to return this response and then so what kind of stuff have you done around mocking so most mostly for my blocks and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:29&lt;/strong&gt; · for all service components which have some sort of dependency like a network dependency or something uh I follow the dependency injection principle and instead of creating a network class class or something um by myself I just um have a dependency on it like have an argument in the Constructor when it comes to testing you can use moito to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:51&lt;/strong&gt; · have a mo class of that for example if we&apos;re doing HTTP requests I wouldn&apos;t even have to mock the actual http request because I&apos;d be mocking the network class which has methods like get user ID and instead of then going through the HTP call I just uh return some mock value which is super cool because uh with moito you can also do of assertions for example that the method which um you think should be called once is actually called once um which is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:22&lt;/strong&gt; · super cool if you have all these mock classes because you can just test your logic component by itself um and just just focus on the thing that matters what what are you doing yeah I was using that package too but the more you talk about the more I realize I&apos;m not using it correctly so yeah it uh that&apos;s that&apos;s a really powerful one um for me I also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:46&lt;/strong&gt; · try to mock out you know my Firebase stuff I know there&apos;s a couple different like mock fire store and um just different various ones for that but by using those yeah um and we we have links in the description but yeah what&apos;s cool about that is like I can mock out my test cuz you know sometimes you have parts of your application that maybe you don&apos;t have test for because it&apos;s code that lives in other packages and stuff like that and I think this is also another point which I wanted to bring up which is making sure that we&apos;re using packages that have tests so that those&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:16&lt;/strong&gt; · are individually testable because you may spend you know hours debugging your application just to realize it&apos;s a bug in a package uh I know the pain yes and I know I need to be you know we always need to add more tests for our packages so yeah yeah it&apos;s tests are hard because if you&apos;re writing code for the package itself you&apos;re seeing the progress you&apos;re seeing cool lights showing up on your phone you&apos;re making stuff but when you&apos;re writing tests you&apos;re not doing any real progress like you&apos;re being a good citizen like you&apos;re doing your tests but nobody&apos;s like a lot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:50&lt;/strong&gt; · of people don&apos;t actually care that much about test and it&apos;s it&apos;s like in an interesting State like everybody is talking about how you should write tests but in reality a lot of people or maybe even most people are not really writing tests and it&apos;s always fun to see yeah well it&apos;s like um it&apos;s instant gratification versus delayed gratification I I when I finish writing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:15&lt;/strong&gt; · a test Suite I do feel pretty awesome but not until I get them all running and debug all the issues leading up to that point because when you can&apos;t get a test working it&apos;s so easy to just be like yeah well I just want have a test for this but you know uh it that that to me&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:30&lt;/strong&gt; · uh it&apos;s you know with yeah you like you said when you&apos;re writing UI it&apos;s super fun and you can write fast you can break things but when you write test you you have responsibility because then now if I refactor the application the test will break and you got to make sure that all those things uh work out which is another reason why I wanted to bring up the um the layers because if you&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:51&lt;/strong&gt; · writing your tests to where you&apos;re testing having unit tests you should be able to change your UI without your unit test failing um because all your logic should not be in the UI layer um which is a huge advantage of separating your application logic out um which I think a lot of people don&apos;t realize yes that&apos;s true like just having the um confidence that the layers you&apos;re building your app on top of are working um helps helps a lot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:18&lt;/strong&gt; · like you know the network layer is always going to work the whatever refresh token is doing its thing the caching works and all you have to really worry about when it comes to refactoring is that looks pretty and and you can be sure that it is correct yes yeah and then also recently I&apos;ve started to te mock like mock the file system and doing tests around that have you worked with that at all I haven&apos;t can you um tell us about about your experiences yeah so uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:47&lt;/strong&gt; · I found it first when I was using the local storage package and what&apos;s cool about it is it lets you test like mock like file systems directories files folders and anytime that you&apos;re using D.O you can actually use this and it creates a virtual file system that you can read and write files to and make sure that you can test to see if the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:08&lt;/strong&gt; · data exists if it&apos;s updated and then that way you&apos;re not actually in a test actually updating any physical files that is super interesting I have some code right now which is doing some file system manipulation and changing moving around file so should probably take a look at this and write some tests for it yeah and it&apos;s super easy too and pretty much like it&apos;s a drop in solution to where you&apos;re just changing the directory I believe so yeah it&apos;s it&apos;s super cool so next what about let&apos;s talk about code coverage have you what do you do for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:37&lt;/strong&gt; · your applications when it comes to that to be honest uh regarding code coverage in Flur I haven&apos;t done too much with that haven&apos;t tried too much um what about you what have you uh used it for and how yeah so uh for me I was really&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:54&lt;/strong&gt; · interested when I saw the graphs on GitHub and I just could not figure figure out like how people could even generate the code coverage so for me it was a lot of like just wow that&apos;s really interesting how can they explore the code and um discover this so I really wanted to figure out how code coverage worked but um through that I found out&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:12&lt;/strong&gt; · that flutter actually supports it directly um so we&apos;ll have this in the show notes but did you know you can just run flutter test-- coverage and it&apos;ll give you a complete coverage graph of all your flutter code and shows how much you have test for interesting how does it behave um with UI like does it also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:30&lt;/strong&gt; · show the coverage of how UI have you have tested with all your widget tests or not sure I&apos;m still exploring it but basically I it reads all your widget tests your unit tests and uh your integration tests and try and gives you a a a map that you can then use I think it&apos;s called lvoc to then generate a graph from but yeah it&apos;s it&apos;s super cool um I mean I think a lot of it&apos;s like fuzzy logic so U but for me yeah it um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:57&lt;/strong&gt; · it&apos;s really really cool how in depth it it went super interesting I&apos;ll definitely have to try this command at some point yes and we will we will include in um in our open source repo of the podcast player we&apos;re going to have this uh code coverage that we&apos;re talking about as well one thing I also wanted to talk about is widget tests um regarding your experiences um for me widget tests are great like um you can run your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:22&lt;/strong&gt; · widget in a simulated environment without having to use an actual device which is very fun but I always had this problem of when the widget test fails it&apos;s super hard to debug because there&apos;s no easy way to actually run the application all of its things like using HTTP and all that stuff and just debugging the widget test&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:43&lt;/strong&gt; · is super hard what are you doing what are you using it for how is your experiences yeah well to be honest I I always write my unit test first so widget test is like a nice to have in my applications mainly because my UI is changing so much much but I do write widget tests and what yeah I have a similar experience it is very hard to debug something that I&apos;ll say that makes it a lot easier is if you use keys with your widgets it makes it a lot easier to find those widgets when you&apos;re testing but yeah it is really hard to debug you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:14&lt;/strong&gt; · have to really know the code inside it out to be able to write the test in the first place which I guess kind of makes sense but if you don&apos;t really know what you&apos;re doing it can kind of be confusing when you&apos;re like okay well I have this screen not really sure what state this the app is in at this point but I&apos;m just going to go ahead and keep trying trying and failing to see what I get um doesn&apos;t isn&apos;t the flutter driver allow you to see your test running on a real device yes so there&apos;s two different uh kinds of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:41&lt;/strong&gt; · UI test for FL you have the widget test which are simulate which run on your computer and they very fast and you have all sorts of different finers you can use because all the code is running in the same process on the other hand you have the driver test driver test run on an actual device and the test itself&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:00&lt;/strong&gt; · communicates with that device using a Json protocol basically sends commands to it uh finders and gets back the results um which is way slower it runs on a real device but it is the actual app like your whole app is deployed on the device and you can tap around um so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:20&lt;/strong&gt; · for the problem with integration test for me is it&apos;s not as powerful as widget tests like there&apos;s a lot finders missing um you cannot uh get get to the data that easily and that&apos;s actually a bit of a problem for me because you have this place you want to have your integration test but also do a real test and with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:43&lt;/strong&gt; · the integration test with the driver test you are very limited in some sorts have you used the integration test before um maybe once like uh and it was probably because it already existed I don&apos;t think I&apos;ve written one personally oh okay the cool thing what you can do with the widget test actually is um you can visualize the UI by um running a command which I will look up and we&apos;ll post them in new show notes um but the problem with that is um normal in the widget test um calls to http I think&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:16&lt;/strong&gt; · also toor own and stuff are forbidden so you have to do some hacking to enable those and when I tried to run those tests there were more errors and failed assertions and stuff then it actually helped so it would be super cool if there was a way to um have widget tests which are actually visually visible and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:38&lt;/strong&gt; · you can debug like writing test for UI without seeing UI is pretty mad if you ask me well isn&apos;t this where I could be wrong but isn&apos;t this where we could take a snapshot isn&apos;t can&apos;t you also take a snapshot of a render object and write that out to a file to see what the current state is at any time you definitely you can um I think it&apos;s always a matter of convenience and how easy it is because to take a snapshot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:03&lt;/strong&gt; · you have to use the repaint boundary you have to have a widget in there you have to look at the file and try to figure out what&apos;s wrong and sometimes it&apos;s just some sort of finder just doesn&apos;t find stuff and you&apos;re like why why doesn&apos;t it find it it&apos;s there and sometimes it&apos;s just super weird to work on UI tests&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:23&lt;/strong&gt; · without the actual UI uh first in mind like uh it would be super cool if you&apos;d have some sort of application which you could drop in a widget or your whole application tap around and it would generate the finer code and when running the test it would actually run the test or if you&apos;re running test in some sort of visual mode it would run the test in normal speed on that program and you&apos;d see what is happening what is uh what is failing well this is actually where I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:53&lt;/strong&gt; · had experience with when it came to you know when I first started with iOS this is something I really loved about xcode unit testing so one really cool thing is like you know when you have a swift file you can just create your know your your normal test uh widget UI layer and then you just at the very top you click record and then you open your application you tap around and then it will record all those and write all the code for you in the unit test so it&apos;ll write you know tap on this field wait a couple seconds fill in the data scroll&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:23&lt;/strong&gt; · up go to this widget tap this and it does all this for you um based on screen events and tapping around I even have done this with flutter applications too to where I&apos;m in xcode it&apos;ll just tap on the screen it won&apos;t actually you know tap on like it won&apos;t give you like tap on UI label it&apos;ll just say tap on this offset but what&apos;s cool about that is I think this should exist we should either make this or this should exist in Dart because I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:49&lt;/strong&gt; · was not afraid to write UI integration test cuz then I could just like you know just click record and then do log into the app log into the app with this account go to log in and then do this page and do this and then do this and the cool thing is then once you have the test done then you could encapsulate it into a method and then call those methods so I could say you know log in with this username and password and then I could use that function to change accounts um as well as you know you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:14&lt;/strong&gt; · just build these building blocks and I was able to you know flush out my entire application in just a day whereas you know it I don&apos;t even know how long it would take me in the flutter widget test to be able to to get the same level of um you know coverage um when it comes to testing for that I feel right now writing these integration tests and widget tests is actually more work than just manually testing your app that it&apos;s working uh yeah I feel like if we would have something like this and for a a lot more people would be actually testing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:45&lt;/strong&gt; · and just doing this because just tapping around in your app and just uh like you&apos;re doing this on on the in the time when you&apos;re manually testing like if you have the same um the same process but write code for it and instead of repeating it by hand repeating by code without any additional effort um we we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:06&lt;/strong&gt; · really need this in FL at some point and it should be possible like I don&apos;t see some technical side which would make this impossible like I think I had this idea a couple months ago and sat down for an hour and tried to hack something together um and I think uh in theory it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:23&lt;/strong&gt; · was all going well and it should totally be possible to just load in your application tap around and then generate the code I think there is um a method in the testing suit I can&apos;t remember right now but it can suggest you finders like you tap somewhere on the screen and prints uh suggested finders in the console which you can use to find that element you&apos;re tapping on you could even use that you could even use that IPI we&apos;ll link this in the show notes um you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:53&lt;/strong&gt; · use that and just generate the code when you&apos;re tapping around and just ass searching stuff that it&apos;s there and you have all the apis with the widget test and the cool thing with this is you could have the full visual mode when recording test but when executing test you could have it all headless and fast like you can be sure it&apos;s working um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:14&lt;/strong&gt; · because you tap around yourself and when it&apos;s uh time to run it it&apos;s super fast yeah maybe we could um if we were to build this we could even piggy back off of the device preview package which then you could run your like basically wrap your material app with this this widget and then you kind of enter a testing mode when you&apos;re running it um speaking of which have you use the device preview package actually I just used it for the first time a couple of days ago and I am amazed like I was always hesitant because I saw the device preview um on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:47&lt;/strong&gt; · the iPad I think on the um official GitHub page it shows it an iPad I always thought okay I don&apos;t have an iPad um and uh this application I&apos;m using um doesn&apos;t have desktop support because I&apos;m not on the master channel so I only have my phone and I was it surely must look weird on the phone but actually looks pretty good in the phone like it&apos;s all skate down but you have all the proportions all the layouts um done correctly and it&apos;s super useful for detecting um bugs on smaller screens are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:17&lt;/strong&gt; · you using it yeah I&apos;ve been using it um for a while now even um am store used it for a little bit but what&apos;s cool about it is um I guess I was able to get it run on Mac OS like months ago because um we had desktop support and what&apos;s cool about that is I was able to resize and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:34&lt;/strong&gt; · free form any type of screen I was trying to test which is super cool um as well as you know being able to change the theme dark mode um you can change the you know the the local so you can even switch languages and it was really good to kind of do all those at runtime&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:51&lt;/strong&gt; · um with his latest update it even um made that a whole lot simpler because then you can kind of just specify key for your application and it um it will kind of inject a lot of that um in a better way but yeah it&apos;s um it&apos;s really nice for testing because something that we don&apos;t always think about is you know you know widget testing is great making sure things appear unit tests are great to make sure functions and methods uh work correctly but then also responsive testing we need to make sure that our UI lays out correctly on all these devices&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:18&lt;/strong&gt; · I mean like just like we talked about earlier like if you have an iPhone 4 and then it doesn&apos;t look good I mean your UI has failed at that point um as well as you know I think it should also be considered a fail when your application is a mobile device you go up to an iPad and you have an app bar and a floating action button in a list View and that&apos;s your entire application like uh you know when we we&apos;re not taking advantage of the the greater space the user is going to hate that yes I also feel like a lot of designs nowadays um only have this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:49&lt;/strong&gt; · perfect world where you have the latest iPhone with the nicest sizes but what happens to the design if you rotate it what happens if you have a bigger or smaller screen and I feel like a lot of the design mindset isn&apos;t there with the responsive design most most of things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:06&lt;/strong&gt; · look good on one screen but you as a a programmer it&apos;s sometimes your job to figure out what happens if it&apos;s on a big or small screen and there&apos;s uh mostly little to no guid guidance on what actually should happen and what the design is intended to to do um which I hope is going to change in the future like there a lot of different device and um FL itself calls&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:30&lt;/strong&gt; · this ambient Computing where you have your application and it runs on all different settings and forms and you don&apos;t fixate on the phone which is I don&apos;t know how big but instead on the design and how data flows and how is it uh how it&apos;s proportional to to one another yeah so speaking of that awesome flutter interact talk they also announc flutter octopus so when it comes to testing all these devices uh what are your thoughts thoughts on the the octopus it&apos;s it&apos;s it&apos;s a great idea like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:59&lt;/strong&gt; · it&apos;s it&apos;s a fun idea um I feel like you can do a lot of this with the software side uh like we just talked about device preview but let&apos;s be honest having this for octopus in your work room just seeing all the devices that must be pretty cool yes it&apos;s a it&apos;s a reason to buy all the devices cuz it&apos;s for testing purposes yes we we need those Chef yeah yeah yes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:23&lt;/strong&gt; · you know but after seeing that it it made me want to write an application that does the same thing but I think there&apos;s some different applications on the web that allow you to do this so you can basically you know enter a URL and you can test it on all these devices at once so then you can just like scroll and you can see iPhone 4 iPhone 5 like iPad you know um Android devices you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:45&lt;/strong&gt; · know General web size layouts and then they&apos;re all in the same state so then you can change the url and then they all update yes I&apos;ve seen it on Twitter I think it&apos;s called Sizzle or something it&apos;s a browser web browser for developers which does all does all of this yeah cuz uh really I mean we don&apos;t&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:02&lt;/strong&gt; · have to have the actual emulators running we just need the correct uh device size and aspect ratio so I think that would be a cool um easy win for testing different stuff because then you can kind of look at it side by side and just see how it kind of all flows together cuz I feel like for a long time like I said before we had desktop I had a hard time when I was developing for iOS because I knew all the dimensions but then you know the Samsung&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:27&lt;/strong&gt; · you know note for example it had the same display resolution as the iPad mini but it looked awful because it was like I had it was like the desktop playout on the Samsung Galaxy Note and then it was like I would tweak it a little bit but then the iPad one would look weird because then it&apos;s like you know so it&apos;s like um uh display density is not&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:47&lt;/strong&gt; · everything and then it was also you know there&apos;s so much more than just checking your break points and so for me it&apos;s it&apos;s it it was pretty big eye opener because because you know you can&apos;t always trust the resolution on what you&apos;re on that is true yeah talking about all of this just makes it more clear to me that we need some sort of application some sort of test suit and something integrated which lets you record test test on different devic and has everything which is related to making it uh responsive testable we need&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:18&lt;/strong&gt; · something like this yes and uh I think we can do better too I feel like it&apos; be cool if we built a a VR sphere where we can test all we build a virtual flutter octopus basically in VR that would be pretty awesome cuz once you get it in there then you can just you know have like the X-Men style you know room that you can look at all your devices and see it all running at the same time yeah I&apos;m just thinking about all the possibilities that sounds sounds super fun yes would be super interesting to try it out and see how it uh it is in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:50&lt;/strong&gt; · production and how people could be using it yeah and I mean a lot of people may think well you know that&apos;s that&apos;s great and all but like who really cares about this well you know the thing is you may not realize you need this until you need it and then when you need it you really need it um for example I feel like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:07&lt;/strong&gt; · another thing that&apos;s missing is and some other um I think Frameworks have this but uh at least with material design it&apos;d be cool if we had like a an inspector kind of view where you could view the elevations in a 3D perspective so you could say okay uh let me turn it on the side and see like how my layout is positioned so I have my app bar my floating action button make sure everything&apos;s hierarch looks correct and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:29&lt;/strong&gt; · um there&apos;s different ways that we can debug our layouts from different perspectives where you realize like oh that&apos;s why I have that bug is because this this column is overflowing right here which I feel like uh hot UI is really supposed to help us with um since I use VSS code it&apos;s not really inside there every now and then I can see it in Android Studio have you used it at all uh I took a look at it once um and it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:54&lt;/strong&gt; · looks PR interesting but just backtracking for one sec with the thing you talked about elevation how cool would it be if you could see your routes being pushed onto each other and just having the whole history in 3D and just taking a look at your screen and seeing all the different routes which are behind that that could be pretty fun&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:13&lt;/strong&gt; · that would be cool then you could basically have your navigation Stacks yeah I didn&apos;t even think about it that way yeah that would be so cuz then you&apos;re like oh well this is when I pop and when I show a dialogue and it&apos;s like oh this yeah that would be really cool instead of having to always call like you know push replacement or different kind of um things that we do to get around or shortcomings but yeah it&apos;s uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:33&lt;/strong&gt; · that&apos;s a really cool example um so when it comes to um testing have you worked at all with Android testing I&apos;ve done Android in the past and I&apos;ve done a bit of testing I know there were a few Frameworks like um testing Android was always hard regarding UI because um in comparison to flor it wasn&apos;t uh code which you can run it was this XML and you had your old your application with it there were a couple of par uh Services I think Robo electric and Fs which um did uh help a lot with testing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:07&lt;/strong&gt; · but it it&apos;s been a while and I don&apos;t know what the current state of testing on Android is and um yes well I actually do have experience with cotlin testing and I think that because cotland now has official support or I know Java it has too but like they they made it a whole lot easier to do testing um I have actually really enjoyed the cotlin testing and the way you kind of go about it cuz you can have a class and annotate a function that you know at&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:35&lt;/strong&gt; · test with The Decorator and then you can write your stuff and then when you run the test on the file it&apos;ll run all the tests for the the test for the logic in which the class exists so you can kind of keep it in one place which is super cool yeah testing like testing non UI stuff was always it&apos;s like it was using Java and cotlin just doing UI test and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:54&lt;/strong&gt; · stuff was always uh great and if you had the right architecture like most of the time I was using mvvm um you could have your view models and all that stuff fully tested um just going to the UI test was always a bit tricky yeah so I had the thought the other day is like what if we worked on like a vs code extension where you could you know how we can extract as a widget or method what if we could do extract a test where you could right click on a a widget or a method and it extracts it to a new test file uh with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:23&lt;/strong&gt; · scope to that exact you know file and then you know that part of your application interesting like if the widget is very modular and doesn&apos;t uh depend on something from provider for example yeah um that should be super easy what&apos;s always a bit tricky for example if you want to test a screen in isolation and it has all those assumptions about your application like for example you&apos;re logged in you cannot be on the home screen without being logged in it&apos;s just not possible things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:51&lt;/strong&gt; · you always have to watch out for so so you include the necessary dependencies in the widget Tre for example um but yeah like uh it could be extension for us code or it could also be a standalone application where you just load in your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:07&lt;/strong&gt; · your application run it and just do it all visually because I still feel like youi testing testing UI shouldn&apos;t be done in code too much it should be oh I see the way you&apos;re actually testing that&apos;d be kind of cool to where basically we could even do it to where you know when you&apos;re running your application you have this method that only runs in debug mode and and you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:26&lt;/strong&gt; · it&apos;ll before your application runs it it runs all these tests for example and then that way if it runs you know that your test have passed if you wanted it to click around on do different stuff is that kind of what you&apos;re saying well I&apos;m not sure like I was uh talking about the thing we talked earlier about the Standalone third party application which loads your application into itself and does all these uh fancy testing things where it allows you to find stuff tap on stuff and produces the tested code maybe&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:58&lt;/strong&gt; · allows you to test scoped stuff like you tap on a widget you say I want to test this disables everything else you have full control over that widget and it uh builds uh the correct widget tree for you in the test so this widget can function user is logged in and all that stuff for example oh very cool yeah yeah I agree would be kind of cool yeah and also um maybe next week we&apos;ll talk more about localization but um when it comes to writing our tests I feel like you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:28&lt;/strong&gt; · know one thing we often forget about is strings in our application because we you know we may provide it or I at least I build it with English in mind because that&apos;s what I can read and then you know maybe in Chinese or Hebrew for example everything&apos;s right to left and our applications have to switch and this is another thing that we don&apos;t think about when we&apos;re testing is have you you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:49&lt;/strong&gt; · switch the directionality of your application and run it because you will be shocked at how many things that you cuz if you do like custom widgets and layout a lot of the times you have to take it take into account so like if you ever do like a positioned at the bottom left when you&apos;re in a right to left layout you need to make sure it&apos;s on the other side so flutter does a really good job to do a lot of this for you like for example if you&apos;re using a row it&apos;ll switch the direction or um just various&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:15&lt;/strong&gt; · uh widgets it&apos;ll it&apos;ll take care of that but yeah that&apos;s something you definitely want to um make sure you&apos;re testing yeah also I think for stuff like alignment petting there&apos;s actually different Constructors and different uh things which allow you to instead of specifying petting left you can do something like petting start which does left on uh the non um inverted uh text text uh things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:43&lt;/strong&gt; · and switches those for for the left to right uh right to left yep exactly and that that&apos;ll really help to um and that&apos;s just about being um more abstract when we&apos;re writing it out yes that&apos;s true um we just want to thank you guys for listening to this episode so what um what are you working on at the moment norber right now just usual work and doing a few um still work in progress&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:08&lt;/strong&gt; · projects which I hope will I&apos;ll be able to Showcase something next week but yeah as I said quarantine life is just going as fast as it as it could what about you yeah quarantine life is the new life but yeah U for me I&apos;m working this week on building a 3D visualizer um in VR that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:28&lt;/strong&gt; · allows you to send in MIDI and it controls um this VR kind of music so that&apos;s been kind of interesting it&apos;s something I&apos;m not very familiar with so it&apos;s a lot of research and trying to get it working with unity and thankfully there&apos;s a lot of repos out there but um if you want to find us make sure you look down in the description for the show notes we have um we&apos;re both on Twitter and GitHub and you can find us pretty much everywhere we&apos;re going to be linking to that um and also we have a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:55&lt;/strong&gt; · link to the podcast as well as our podcast player which we will link to which is now open source and we will let you kind of see how you can actually create a podcast and host it on GitHub pages and we have all the details about that so make sure to give us a rating on iTunes Google play Spotify and anywhere else we have our podcast and we just want to thank you so much for listening and see you next week see you next week&lt;/p&gt;
</content:encoded></item><item><title>Talking about Riverpod with Remi Rousselet</title><link>https://rodydavis.com/podcast/creative-engineering/talking-about-riverpod-with-remi-rousselet/</link><guid isPermaLink="true">https://rodydavis.com/podcast/creative-engineering/talking-about-riverpod-with-remi-rousselet/</guid><description>A discussion about state management in Flutter, focusing on Riverpod, a package created by Remi Rousselet to improve upon the functionality of the original Provider package.</description><pubDate>Mon, 04 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Talking about Riverpod with Remi Rousselet&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;qyfdowkmtAk&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Diving into the details about Riverpod!&lt;/p&gt;
&lt;h3&gt;Remi Rousselet&lt;/h3&gt;
&lt;p&gt;https://twitter.com/remi_rousselet&lt;/p&gt;
&lt;p&gt;https://github.com/rrousselGit&lt;/p&gt;
&lt;p&gt;Norbert Kozsir - @norbertkozsir&lt;/p&gt;
&lt;p&gt;https://twitter.com/norbertkozsir&lt;/p&gt;
&lt;p&gt;https://github.com/norbert515&lt;/p&gt;
&lt;h3&gt;Rody Davis - @rodydavis&lt;/h3&gt;
&lt;p&gt;https://twitter.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://github.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://youtube.com/rodydavis&lt;/p&gt;
&lt;p&gt;https://rodydavis.com&lt;/p&gt;
&lt;h3&gt;Our podcast player&lt;/h3&gt;
&lt;p&gt;https://rodydavis.github.io/creative_engineering/&lt;/p&gt;
&lt;h3&gt;Follow on Twitter:&lt;/h3&gt;
&lt;p&gt;https://twitter.com/CreativeEngShow&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · welcome to creative engineering today I have a special guest with me you may know him from stof or one of his very popular flutter packages I&apos;m of course talking about Remy Remy thanks for being on the show how are you yeah thank you for ring me uh I&apos;m doing pretty good what about you I&apos;m doing pry good too so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · today is going to be all about State Management and your newest package called River pod to start things off why did you call it River pod um yeah so provider but with the letters mixed up and there&apos;s also a second meaning um where you know in in Dart we have streams and so I I realized that by&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · mixing the letters we could have River in the name so it&apos;s like in the same team so yeah there&apos;s that I like it very much it&apos;s a pretty pretty fun name so before River pod you create a provider um provider is used in pretty much all fler projects where is directly or indirectly via for example FL block what was the reason for you to start working on this new River pod um it started uh usually it started&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11&lt;/strong&gt; · with slacker flow like at some point I answer a lot of question on sa flow and I realized that the same question was popping up all the time about inated widgets and or people misusing ined wies usually uh like they created some State dark inside the build but then when something changed their state was lost because they didn&apos;t use a state for widget or whatever um so what I did was um first I answered&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44&lt;/strong&gt; · the questions but you can pretty fix the problem before uh people face the issue um so I searched for a different way of solving this problem before it happens um so that&apos;s how&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01&lt;/strong&gt; · provider came up um at that time it has some some a provider named state for provider so it created like it&apos;s kind of this it&apos;s similar to the syntax with um the current state of Provider where you have this create function that creates an object only the first time it&apos;s created so the idea was behind it was that people only use this uh syntax this way their state is not last when something change uh and then you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:30&lt;/strong&gt; · time as um time passed uh more use case were added and um yeah that&apos;s basically how it started interesting so you touched upon this and it&apos;s a very interesting distinction what do you think about this what is River pod SL provider really is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:52&lt;/strong&gt; · it dependency injection is it State Management is it architecture because I feel like um those three topics kind of bleed into each other and provider and River poot um can be used for multiple things or for same thing what is your opinion on this um I mean I know that many people uh consider provider R not State Management&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · U but I quite disagree with that statement in the sense that even if we are talking about just injecting variables that&apos;s State Management itself right State Management is a very very broad topic and I would go as far as to say that widgets in FL are State Management and when you think about it that do nothing else but managing states that not painting anything are not uh under under links clicks they&apos;re just managing the state of the UI um so yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:45&lt;/strong&gt; · in my opinion um provider is State Management is just a specific a very specific portion of State Management where uh it&apos;s initializing the objects and passing them around um and then we have R which does um which has a slightly broader scope where it&apos;s also like it&apos;s doing a lot of stuff for combining objects and um like caching results when you&apos;re combining things um filtering reveals um it also has some more opinion&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:15&lt;/strong&gt; · rated um things like State not State provider which is a very simple provider to create variables so it&apos;s it definitely has a broader scope and I would think I think that more people would consider about an actual State Management solution interesting the reason I was asking because a lot of um use cases are a common thing when using for example the third BL package was to use the block package as State Management and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:46&lt;/strong&gt; · block internally uses provider to provide its to its descendant so the idea with rivert is to have um the idea of dependency injection and State Management more tight ly uh wouldn&apos;t say&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:02&lt;/strong&gt; · coupled but having a package which is more focused on this um my question in there would be I&apos;ve used River pod myself for um my recent project and there is a provider which is explicitly used for the state um I think it was a state provider for your state how how&apos;s the package called yes the state Notifier so in this case if I understand correctly the state Notifier would be the core State Management thingy and provider would be&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:34&lt;/strong&gt; · H not provider River pod would be a way to combine that state to provide that state and to manage that state how would you um yes and no um like yes uh this state Notifier would be your core class to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:53&lt;/strong&gt; · like add your typical increment method in counter for example uh so that&apos;s a way where you can can Center all your business logic inside uh but providers also um are a place where you can put business logic inside like you can start HTTP request or things like this inside providers like typically with feuture provider for example you can do a a complete um pagination uh system with just using&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:21&lt;/strong&gt; · feure provider with no state notif or whatever so you can get really far with uh with this um so I would argue that providers also are are um State Management it&apos;s not just specific to one class I see so it can be used I guess&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:40&lt;/strong&gt; · it&apos;s like a lot of topics in computer science and programming the answer is it depends um it can be used for certain State Management things for some it might be easier because it is doesn&apos;t require to overhead I would I would say that um the only reason currently when why we currently need this statement if your class is because we don&apos;t have um Anonymous classes in dark if we did have these Anonymous classes uh and maybe s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:11&lt;/strong&gt; · dollar classes and S classes in dark uh we probably could remove St if you&apos;re completely and just use providers but wouldn&apos;t it be possible to have these kind of Anonymous classes with the with functions with Anonymous functions because you can actually declare functions inside functions and have we can definitely do that but then it looks very scary and few people use&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:37&lt;/strong&gt; · it like River already looks quite scary to to some people so I think that if we want to go a step further it would be too far for some people like I know for example that people complained about having too many providers and while I disagree with u River being complex I think it&apos;s just because it has a broad scope so um so uh people kind of think that it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:03&lt;/strong&gt; · complex just because there are uh many apis um but yeah um it&apos;s very difficult to have something that people uh would not fear like I I I think that functional programming for example is something very simple if you try it like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · especially like for example with Elm or other uh simplified version of functional programming like the tooling helps you so much that you can almost disable your brain and still do things very rapidly because the compiler uh tells you every mistakes you made so you just have to almost uh do right click and fix and it does the same thing for you um but people can of have this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:50&lt;/strong&gt; · mentality where oh no it&apos;s functional programming so it must be super difficult I feel like functional programming um does all the things for you and in some way which might also be a good thing it makes um doing hecky things like something that has to be done quickly which just works it is bad&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:09&lt;/strong&gt; · but it works it makes it more difficult because you cannot just mutate something which in turn might be useful if you just want to try out a little thing you want to build a prototype you just want to get it out quickly without caring too much about architecture I think um functional programming makes things like this hard but on the other hand it Mak makes it easy at some point to scale to debug and to do all of this yeah yeah I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:35&lt;/strong&gt; · definitely agree with this statement which is why I think that um the ideal scenario uh when building applications is nether using pure object oriented programming or pure functional programming but using kind of like an hbd architecture instead uh especially I think Dart is a good candidate or maybe cutl whatever is we have this trend recently with language set to Bow and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:04&lt;/strong&gt; · where we can gain both the benefits of imitability and mutability when you want to and Flur use this quite a lot actually like where with widgets we have this uh IM imitability um layer but then if you go inter if you go one layer deeper with runer objects for example they use mutability quite a lot to have this um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:29&lt;/strong&gt; · very performant API where instead of recreating everything every time you just mutate what needs to change so uh you use both sides of the both benefits like you have this immutability side for maintainability and this mutability side for performances and I think it&apos;s a good compromise and um that&apos;s kind of the thing that I that&apos;s a mentality that I try to add in River I think it tries to do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:55&lt;/strong&gt; · the same thing where you could technically for example um Mak mutability and immutability if you want to you could um have this U declarative programming where uh things just like you know uh top down and kind of working&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:15&lt;/strong&gt; · by itself like you you write it and it re execute when when it needs to re execute by itself just because um you know kind of like with Widget you don&apos;t really listen to anything specifically it&apos;s just working by itself just by writing the stream Builders or whatever um mhm so at this on one side you have this aspect of declarative and kind of imitability too um and on the other side you can go with um I don&apos;t know change or state interfer and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:47&lt;/strong&gt; · go um with a very fine gr control over your state um yeah yeah I really like the idea of being able to adapt these different paradigms like the function programming and the object oriented programming in each other to have like the best of both wordss like have as you said render objects being these objectoriented State machines which heavily rely on method calls to do stuff&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:15&lt;/strong&gt; · and on top of this we just have this functional layer um which harvests all the power of the objectoriented layer below it but ads on top of it that&apos;s a very powerful powerful idea yeah um coming back to the actual River pod we briefly talked about this before you said people felt intimidated by having all those different providers um I can somewhat understand it I&apos;ve been reading through the docs and like okay State provider does this and the normal provider so what are the different&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:48&lt;/strong&gt; · providers there are in River pod what do they do and why do they exist um there are a few of them we likely I will have more in the future so I&apos;m not sure that uh list is really helpful but um this short story is we have um provider uh just provider not&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:11&lt;/strong&gt; · State provider or whatever just provider um which is used to create um an object that never changed by itself so it&apos;s just like you know a repository or an HTTP client if you want to um or maybe a computed value if you want to because you could technically listen to other providers when creating a provider um so you could make like um a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:37&lt;/strong&gt; · filtered list of toos so you take you have your provider that listens to the too list and and listen to the filter and combine both variables to have the filter to the list um which will then rebuild only one other of the dependency change so you get these benefits of not filtering your to list directly inside the bill method which is a lot more efficient um so that&apos;s for provider uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:03&lt;/strong&gt; · then State provider is typically for very very simple States uh that don&apos;t really have business Logics associated with them so like filters from the UI it&apos;s just an Nom and you&apos;re just changing the enom directly or switches if you want to um then by putting this logic inside&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:23&lt;/strong&gt; · providers uh it allows you to combine this state with other things so like I mentioned previously with the to list example if you have your um filter implemented in a state provider you can then combine it with the state of your to list to to this um very powerful filter mechanism um then you will have uh your typical business logic providers so St&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:52&lt;/strong&gt; · your provider change notif your provider um maybe if you using AER body packages I&apos;m pretty sure uh maybe in the future we&apos;ll have block providers we don&apos;t know um which have this fine grain control over a piece of state where it has methods it has maybe it receives events um and it allows you to mutate the state using uh us this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:18&lt;/strong&gt; · method and EV um it change over time and then you have um dump down versions of uh this business logic um objects with a stream provider and future provider which are like um simplified version of a stay a change of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:36&lt;/strong&gt; · time but for very specific use case so here&apos;s just listening to the result of a future or stream and so whenever something change the stream it will update the value so you don&apos;t have to manually subscribe to the stream and then imit the new value you could do it with a stat or chent or whatever but it&apos;s just kind of like this Bor play that you don&apos;t really care about so making a provider makes things easier especially with Firebase for example so basically correct me if I&apos;m wrong provider is the base class that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:08&lt;/strong&gt; · can be used to achieve all of this for example if you want to have a stream which you listen to you could just provide the stream with provider and then manually listen to it and do all your stuff and stream provider adds this abstraction on top of it which says okay&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:24&lt;/strong&gt; · uh we don&apos;t provide you with a stream of items because obviously what you want to do is you want to listen to those we provide you with every single item and we will provide it every time the stream that internally emits so all the providers like stream provider future provider State Notifier provider could be implemented with more code with just using provider yeah um just using&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:49&lt;/strong&gt; · provider itself may not be the correct choice I think um the correct would instead be maybe State Notifier provider or CH Notifier provider uh because these are uh the most powerful providers uh provider is doesn&apos;t really have a built-in way of emitting update so you won&apos;t be able to implement stream Provider from provider for example but with stat if your provider or change if your provider you could do basically everything so one thing um that I faced&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:21&lt;/strong&gt; · when using provider for example I had a um State Notifier provider and I wanted to obviously listen to the state so so because I was using flare Hooks and a build method I said okay use State Notifier provider so I got the current state now I also wanted to access the actual State Notifier to call method on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:42&lt;/strong&gt; · like update something delete something so this time I couldn&apos;t just use the state Notifier provider because it provided me with the state and not the actual object so what I ended up doing is just calling I think it was something like provider off and then calling the me the method manually um would there be a way to say Okay I want the state Notifier I want all the new States but I also want x to the objects to reduce B plate code um when you&apos;re using state if your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:12&lt;/strong&gt; · provider in RB um you have a way to specify what you want to obtain so when you combine it with I don&apos;t know use provider or the watch method in Rod you can either do like watch my provider or you can do watch my provider. state so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:32&lt;/strong&gt; · if you do the former you will obtain the statement instance without listening to the state so you can then call increment decrement whatever and if you do the later you will be you will obtain this state directly but not this state interfer instance so just talking all the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:52&lt;/strong&gt; · different providers and the idea behind this it feels like it isn&apos;t too difficult like at the end it just BS down to when do you want to be updated for example a stream provider every time um an stream item is submitted or do you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:07&lt;/strong&gt; · just want to have the raw State Notifier as an object without listening to the states so you were talking about adding more examples maybe it would be interesting to have a little tutorial doc showing how to do something which you could be doing with a stream provider without a stream provider just using as little amount of providers as possible and then saying okay we did this this is quite a bit of boiler plate code you could use this to make it easier yeah one of the thing I&apos;m considering is um architecturing my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:39&lt;/strong&gt; · tutorial such that we start without the provider and at the end of the tutorial we have this refactoring step to show how we can reduce the boiler plate to reach the same thing so like we could yeah we could maybe start with I don&apos;t know change Notifier that does the data fetching and that call notif find listeners when um the the query is finished and then at the end refactor to Future provider to remove almost all the C yeah I feel the thing that is most&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:09&lt;/strong&gt; · valuable with this approach is if you get a solution for a problem that you never had before you accept the solution but you may run into the problems that it the solution tried to solve to begin with if you never faed a problem before you cannot really understand why it is there so if you say okay we this problem&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:29&lt;/strong&gt; · how could we solve this solve it first so the user understands okay this is how I would do this this is something I understand and then reason okay we did this and this is my solution for this exact problem yeah I think that is very valuable and that would be very nice for docs um in the dark dock of Herod the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:48&lt;/strong&gt; · documentation Associated to each um element of the API um if you go on the dark do of any provider each provider explains what the use case of the provider so like if you go on the documentation of future provider it will tell you that it&apos;s usually used for fetching a request um fing something for an API or yeah pagination whatever even&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:12&lt;/strong&gt; · if you go on the documentation of family it explains why what is the concept of family why you would need to to use this kind of thing uh I think one of the issue is that the current website um doesn&apos;t show the providers uh doesn&apos;t highlight the mation Associated to each provider in um so people don&apos;t necessarily find it and I think that would be one of the D sides of D dock it&apos;s like kind of hidden in a corner I&apos;m pretty sure that few people use D&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:41&lt;/strong&gt; · dock yeah yeah coming back to the river pod and how it works um I had somebody on Twitter asked where does River pod actually St state so River pod works by&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:58&lt;/strong&gt; · defining Global providers um at first glance it looks like the state might be in some kind of Singleton some kind of global variable um because you are defining the provider AS Global State um how does all of this work where does the State live why is the state where it is there is actually no Global state in rot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:18&lt;/strong&gt; · which is kind of the most imp impressive thing in my opinion with the architecture where even if your providers are Global the state is not Global it&apos;s since that stored inside this provider scope widget that we added inside our widget tree previously and if we want to go more specifically into how it works&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:39&lt;/strong&gt; · um provider scope what it does is that it creates an instance of the object called a provider container which doesn&apos;t depend on Dart and which doesn&apos;t depend on FL sorry uh which is a pure Dart object that contains um States for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:56&lt;/strong&gt; · providers and it it Expos it to the widget tree using plain unated widgets and which what this means is that for example if you were to do testing um you would typically create this provider container object yourself inside a test and this Pro container object contains method like read so you can do container. read my provider to obtain the state of a provider and so the basic idea behind this is that uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:27&lt;/strong&gt; · you know one of the basic criteria for testing is that two tests should not share State because that would be a bad IDE like if you change the order of the test you could have a different Behavior so that&apos;s a very uh so then your test will not be uh reable and so uh using this architecture you could create your provider container uh individually for each state such that um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:54&lt;/strong&gt; · two tests would not share State at all which would would not be the case with Singletons or Global states by the way I really love the approach of having this provider container that doesn&apos;t depend on fur um in my current project I&apos;m actually just started writing a CLI version of the project and I was able to reuse all the dependency injection wiring which I had set up before for the repository for the hdp client and just reuse that in plain&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:24&lt;/strong&gt; · Dart because all the dependencies were wired up using the provider container I didn&apos;t have to do some manual Constructor injection so that&apos;s a big plus another thing I really like and I don&apos;t feel you emphasized enough is because um because you have these Global providers you get complete type safe access to the thing yeah so and all the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:47&lt;/strong&gt; · IDE integration before with provider you had to add this generic type the compiler couldn&apos;t make sure that the um that the actual type is in the widget Tre that you have access to that is there the new provider all you do is I I think of the name okay of repository I just start typing repository and it auto fills to repository provider yeah which makes just so much more productive yeah I also make sure that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:14&lt;/strong&gt; · you cannot try to read something that doesn&apos;t exist or like we provider so many people faced uh provider not phone exception because they use the wrong build context but with River pod you almost can have this issue um you can still face this issue if you&apos;re using like scoped provider which is a very very specific feature in rivot which you probably don&apos;t care about um if you if you want to the short story is that it&apos;s a reimplementation of the four uh fures&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:48&lt;/strong&gt; · of inher widgets with River pod so you can scope them to a wget tree um but then yeah otherwise uh outside of scope providers you can do Bic you you don&apos;t have these exceptions anymore and it also solves these problems about um like for um I don&apos;t really remember&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:12&lt;/strong&gt; · what I wanted to say sorry it may come back in the future that&apos;s perfectly fine yes so actually you you just talked about the um scope provider yeah and there was a thing I was wondering about I haven&apos;t tried using the scope provider yet but most of the state typically lives at the top of the tree because in Florida you have the routes and if you want some state to be available every page on every dialogue you have to put it on top of material app I had an issue open on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:44&lt;/strong&gt; · GitHub that I would like to have some way to share some sort of skate between multiple pages but right now with the current mation that is not really possible so but sometimes a river pod and providers also just used to provide a value to the sub widget Tre for example you have a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:01&lt;/strong&gt; · one page that doesn&apos;t navigate that doesn&apos;t move around it&apos;s just one page but it has a lot of sub widget and each each of those sub widgets has of widgets and you you would like to have value there without having to pass it down the widget tree to every single Constructor the scope provider as I assume allows us to add this dependency which uses the New River pod API to inject the dependency is it right um yes it is right but you probably&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:31&lt;/strong&gt; · don&apos;t need to use cope provider to do that you could technically have your typical provider but just use it inside your specific page and maybe even uh place the provider directly next to the uh implementation of your page and make and make it private you could technically do that especially considering now you have now you have the variables for your provider so you can make them private if you want to which is very powerful if if you think about it um the use case of scope&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:59&lt;/strong&gt; · provider would more be about making team for example so that you can override the team on a specific part of the application uh or maybe um translations or I don&apos;t know very specific use case and other common use case with SC provider is um lists so if you want to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:17&lt;/strong&gt; · pass the ID of an item instead of passing it to the Constructor of the widget you could expose the ID using scope provider and then the items will obtain their ID using the r River API rather than their Constructor the the main benefit of that is that then your provider uh no then&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:38&lt;/strong&gt; · your widget doesn&apos;t receive any parameter at all which means you can make it constant so you can you can make it constant so that if you update wherever inside your list your widget will not rebuild unless what it used rebuilds so if you have like say a list of 100 items and then you add a new item&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:58&lt;/strong&gt; · that means that the item count changed uh but if your item count change um typically what this VI would do would be reexecute it and buer uh for everything that is everything uh that is present in the list until um reaching the end of the screen so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:20&lt;/strong&gt; · that would uh rebuild a lot of things for no real reasons because uh nothing changed for them but if you use scup provider to pass the ID you can make your widget const your items constant and then they won&apos;t rebuild unnecessary&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:36&lt;/strong&gt; · because since they are constant FL is clever enough to not rebuild them so you can do this you can do this performance optimizations but that&apos;s a very Advanced use case I would say like if you&apos;re just getting started you don&apos;t really have to care about that and go back to it in the future but super interesting I hadn&apos;t thought about this uh potential issue and a potential solution about this that&apos;s very interesting um coming back to the um scope and the um scoped&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:04&lt;/strong&gt; · provider discussion we had before for example you have some sort of variable um which you only want to have uh be accessible on this one screen so you could make it private how would you deal with for example initialization and tear down for example you have some heavy object you have a connection to database or some sort of heavy calculation that you don&apos;t want to maintain pain after the screen is for example destroyed something else is pushed how would you deal with this if it is not actually scoped to the screen um one of the very&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:39&lt;/strong&gt; · powerful addition um of the inherited widget mechanism that is implemented in River pod is um the autod dispose flag on providers um basically what it does is that it&apos;s a slightly different implementation of uh when the widget is removed uh with dispos the resources instead what it does is that when a provider is no longer used it it it dispose the resources which means that technically if you remove the page that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:11&lt;/strong&gt; · will automatically dispose the resources too but that that&apos;s slightly different in the sense that it&apos;s not scoped to the page uh so if you for example you have two screens that you&apos;re using the same data then if you close one screen but the other screen is still open that won&apos;t dispose the data immediately because it&apos;s still in use and then as soon as you remove the other screen that will automatically dispose everything uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:37&lt;/strong&gt; · correctly that is super interesting yeah that sounds like a pretty pretty solid approach yeah um and an interesting aspect of that is that we can also combine providers so we can also have this very big graph or different states and so um when you&apos;re using autod dispose um um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:57&lt;/strong&gt; · the autod dispos flag um it will automatically force you to have all the children in the graph of providers to also use the autod disposed flag because you know if if a descendant is not using the autod dispos flag but a parent is using it that would kind of be weird because it would be always kept alive so um so then you have this graph of autod dispose objects and that certainly makes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:24&lt;/strong&gt; · provider uh no River but a very clever like in the sense that uh all your graph of providers will still be in use um even if you&apos;re using only uh the leaf of your graph and as soon as you stop using the leaf of the graph everything will kind of destroy itself naturally without having to care about it mhm that sounds pretty pretty&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:47&lt;/strong&gt; · clever yeah so touching upon this we we talked about this before like the um scoping um not not the scoping where you declare providers one thing I also really like is for example I have a repository and if you if I want to provide this repository to the rest of the app I don&apos;t have to go into for example my main file create a new provider in my multi-provider and provide it in there but I can just do it above the actual class so if I for example say okay my repository now also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:17&lt;/strong&gt; · needs a dependency on the network client I don&apos;t have to move to do different files to do this I can do it all in one place and I really like the approach of having code that is modify together be together for example if I modify the provider and one of its dependencies in the class I also want to modify the actual provider which provides this class M so that&apos;s I really like this yeah we can even go one step further and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:44&lt;/strong&gt; · like now that providers are variables we can even extract them into packages so if you&apos;re spitting your application into multiple packages you can probably extract all the logic for the homepage inside a separate package share it with different thing and you can extract the provider too so that um you just have to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:03&lt;/strong&gt; · insert the provider scope with it at the root of your application and it doesn&apos;t matter whether the homage is using providers or not um it will just work out of the box you don&apos;t have to worry about inserting something inside your main application and another benefit is also in testing like I one of the downsides&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:23&lt;/strong&gt; · with provider is that you have to uh when you want to do testing you have to reinsert all the providers if you want to set to to to test a very specific widget so that so that&apos;s quite a lot of Border PL to add all the providers needed by your wget tree whereas with uh River you can just add this provider scope wet and then you don&apos;t really have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:45&lt;/strong&gt; · to care about reinserting all the different providers that you need to use and you can just override only the provider that you want to Mark the behavior yeah you don&apos;t have to re imp all the wiring which you are doing already yeah because the wiring is done implicitly yeah which which is very important because you likely want to test this wiring too so if you have to copy paste it that&apos;s not a good approach yeah there is um I think always&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:15&lt;/strong&gt; · a good indication that the code might have a few B smells if you have to copy paste stuff to get it to work so this approach now yes I do too so um I asked on Twitter uh what people would like to um hear in this podcast and the majority was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:37&lt;/strong&gt; · examples a lot of people said of course basic counter application can be easily done in pretty much any State Management too where it be state for Widgets or block coun example is a good way to start but there is always uh a need for more so uh in this segment I&apos;d like to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:57&lt;/strong&gt; · walk through a few simple example scenarios simple in a way to simple to understand but from an architectural standpoint maybe not too simple um and I would like to hear your thoughts and how you do this um on those so let&apos;s start off with something actually very simple um just a bit more complex than the counter application the classical Todo um List application but with the little twist that we also want to have items filtered&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:28&lt;/strong&gt; · which I know we talked about is a very good example of Provider it&apos;s actually implemented on Z rivot documentation there is an open source implementation of it it&apos;s just not associated with a tutorial that explains step by step how to do it but if you go to to-dos do R.D&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:46&lt;/strong&gt; · you will see a to-do application with filter and everything implemented R but yes yeah do we still want to go over Concepts or would you that okay yeah so we have a simple application to-do list we want to be able to add a to-do item remove a to-do item and update a to-do item every to-do item is just the text and whether it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:10&lt;/strong&gt; · done or not done how would be the it doesn&apos;t have to go into the detail because obviously listening to code is a bit harder to read it but how would you architect this and what kind of providers and why would you use yeah um I thingss are two difference to there are two big steps there is the filter which is uh one thing on its own and then the um just managing the list of to do so first for managing the list that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:40&lt;/strong&gt; · a typical use case for um a state Notifier or a change Notifier depending on whether you want to use multip or not um so you make a custom statement if your class which expose uh you know add too remove too edit to-do methods and that just expose a list of um to-dos then you pug it into a statement provider or a CH provider uh which then expose uh which then allows your UI to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:13&lt;/strong&gt; · listen to the list of to-dos and do something on click or whatever to update this list of to-dos um so that&apos;s for the list of toos without filter and then for the filter um using Riot is actually very easy to do uh to filter States um so you the only thing you need to add is create a separate um State for the filter um for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:41&lt;/strong&gt; · your enm for example so you can use State provider like we mentioned previously which just stores the current state of the filter so likely none by default um then you plug it to your UI such that it change when you change the filter State and finally you can combine your um filter and your um to-dos your stat&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:07&lt;/strong&gt; · interfere to-do uh to- list using a provider and then inside your pro proor what you do is you use this R object pass to the provider uh and call r.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:18&lt;/strong&gt; · watch uh to-do list and ref. watch um filter uh provider and then you just do um list. where and then filter accordingly and this will automatically um oh yeah I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:35&lt;/strong&gt; · forgot once you&apos;ve created this provider what you do is inside your UI instead of listening to the entire list you now listen to this new providers you just created and so with just this little change and now your UI will automatically filter things accordingly and it will filter the list only when as you added a to do or you um change the filter State mhm so the idea behind this is we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:04&lt;/strong&gt; · could actually implement the filtering inside the state Notifier we could have a method called set filter level or set something like this and then do the actual filtering in there too but the behind the idea behind this is the separation of concerns like you have your state not the fire provider your state no fire that&apos;s not really State uh that&apos;s not really uh separation of concerns here it&apos;s more about making it declarative such that uh we have a single source of Truth for the list of to-do like this this business subject um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:36&lt;/strong&gt; · manage the list of to-dos uh directly and we don&apos;t really want to mess around the um official um SF of toos we don&apos;t really want to changee the public API about it to avoid like having to I I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:52&lt;/strong&gt; · think I saw many questions on sa flow where they did this mistakes where they updated the Leist stuff to-dos that they had when they change the filter but then when they when they go back to none they realize oh no I lost my toos where they are so um yes so it&apos;s so like by separating things uh and using ref.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:14&lt;/strong&gt; · watch instead of having all this logic inside um State notif here you could technically do that and have a big stat if your objects that expose many different things like the filter list and the rooll list and whatever you could do that um but the issue is it&apos;s likely more complex because you will have to deal with listeners you will have to um make sure that you&apos;re caching&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:37&lt;/strong&gt; · the value when when you need to like listening to both the filter and and the change on the uh to the list could be quite complex whereas um by using um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:52&lt;/strong&gt; · this by splitting it into um multiple provid than using r. watchat it&apos;s very linear you don&apos;t really have to bother about uh what happens when the state of the filter change what happen when the list uh change it&apos;s just done for you automatically and without having to care about it I think the thing or the um the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:13&lt;/strong&gt; · idea that would better encapsulate this is the single responsibility pattern in this case because you would have this one component that it&apos;s only its only responsibility is to manage a list of to-dos update and do a lot stuff it doesn&apos;t really have to do the filtering because it&apos;s just has a single responsibility and then you add something else which has also a sing responsibility it gets a list of items and just filters it the interesting part about this it is very um pluggable like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:41&lt;/strong&gt; · if you want to change it if you want to remove the filtering you don&apos;t have to go into your class and remove lines of code and do your stuff all you have to do you just don&apos;t listen to the filtered the filter provider anymore but instead the normal to-do list provider and another benefit is that it&apos;s also more performant in the sense that uh especially if you&apos;re using imitable data like if you&apos;re using imputable data you might if you want to have a single big&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:09&lt;/strong&gt; · uh statement if your object uh your model will likely be quite complex like you will need to have a zero um unfiltered list of to-dos and then you will have to have this filtered list of to-dos but then when you want to do any edit you will have to always is clone&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:27&lt;/strong&gt; · the object but that means that the larger your model is the bigger the Clone operation is whereas if you&apos;re separating it into multiple providers uh you have less things to clone when you&apos;re updating the list so it&apos;s technically slightly faster too interesting okay so I think that was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:51&lt;/strong&gt; · a pretty good breakdown of the um first example of just a simple list of to do we&apos;ve also the added uh feature of being able to filter um let&apos;s move on to something a bit more complex and the ideas or the app would be you have a list of items just like in the to-do list but in this case those items are not as trivial and simple as to-do items&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:14&lt;/strong&gt; · in this case we can have one class which man manages a list of to-do items because they&apos;re so simple it&apos;s just mutable data which can be copied and changed let&apos;s have a case where each item is super complex each item managing its own vast and big um state may do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:32&lt;/strong&gt; · additional Network calls in there to lazily load even more data or just have a very complex interaction I think putting that all in one class might be too much how would you do this um I don&apos;t really understand what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:47&lt;/strong&gt; · you&apos;re explaining so for example think we have a list of not to-dos but of games we have um like 10 games each can be visible at the same time obviously a game is very complex you don&apos;t want to have all the game states of all the games in one class because every item is complex how would you do this okay um yeah so it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:12&lt;/strong&gt; · not just a list of um dumb objects it&apos;s a list of objects that may update independently that has different roles too so it&apos;s not just the same kind of object okay um um um so I think um different approach is uh to um have a separate provider for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:36&lt;/strong&gt; · like when you have um this kind of use case you may want to instead of storing the list of games just store the list of IDs of games and then um so you can update just the IDS new IDs remove IDs and then the listening of um the object is is done separately um so uh the idea you you may want to use something like family um so you want to create um separate uh game uh provider&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:09&lt;/strong&gt; · that is as CH your provider or techn provider and use the uh dot family Constructor to um create your game instance based on the um based on the ID&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:32&lt;/strong&gt; · um turn I&apos;m not sure I think do do you want to add new games at at TR time possibly like imagine it not just being games it there are apps with just complex interactions within this items like not all apps just have this dumb data sometimes it&apos;s just just complex so think of this game as being being anything that is complex so yeah adding removing updating um would be would be something the one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:10&lt;/strong&gt; · wants um I mean uh the the reason I&apos;m um thinking about it is that with games they have completely different implementation so it&apos;s a bit like depending on the game type you may use a completely different controller so that&apos;s speciic use case for example it&apos;s always the same game like it&apos;s Tetris or&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:30&lt;/strong&gt; · something but obviously with a different state like different instances of the same game yeah that&apos;s that&apos;s an example yeah so in that case the uh what I was explaining previously totally makes sense where you have your list of IDs on one side and on the other side you have um your statement if your provider or CH provider. family and then um it receives&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:54&lt;/strong&gt; · the uh game ID um because families are just providers that receive an extra parameter to do some extra fancy stuff so here it&apos;s just the ID you don&apos;t really care about the ID inside the game instance it&apos;s just uh the ID Associated to the to the game and so your state notif provider will create the game instance so the the tetris instance and then your UI will be able to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:20&lt;/strong&gt; · um like when you want to add a new game what you will do you will just uh push a new ID to the list um and then that will automatically update the UI that listen to all the IDS so you you may have a list view somewhere which will update because you added a new ID and then when you want to read the game Associated to the S ID you will um read the game provider that you created which is a family so you will have to do like watch game provider and then uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:54&lt;/strong&gt; · pass parameters to the game provider so you pass the idid that you received previously from theist View and so by doing that U that will uh the first time you try to read this ID that will create the T instance automatically for you and that will also subscribe to the T instance such that when it change change are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:16&lt;/strong&gt; · propag propagated and also think that this makes a lot of sense because the object which manages the list doesn&apos;t have to load all the items and wait until like five instan of Tetris are loaded it just says okay we have five items we can distinguish between those items and the actual loading which may include a loading indicator or some sort of network fetching can be done externally in these Tetris providers yeah so this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:44&lt;/strong&gt; · can be so again single responsibility we have one list that is being managed and we have one individual game that is being managed which could also live on its own yeah so um to make things uh even further complicated because that&apos;s the thing I like is um okay we have like three uh Tetris games and now we want to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:08&lt;/strong&gt; · implement some weird impossible game that accesses the state of another Tetris instance think you&apos;re playing Tetris on free Tils and you want one Tetris to see okay if the other Tetris just spawned a cube I want to spawn a a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:25&lt;/strong&gt; · something else how would you do something like this can you still add tus at run times of course um so how does the interaction between tet instance works like what how how does it determines which tus instance does it want to use I mean an example would be would be&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:47&lt;/strong&gt; · at at the initialization stage you would be select another tetr sance maybe viid so or example you always want to use the instance which is on top of it in the list just some ristic I&apos;m asking mainly because um there is a very important part in State Management which I think is not used in us which is uh un generational data flow um and uh providers uh can of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:16&lt;/strong&gt; · enforce you to use Ral data flow to make sure that your application uh doesn&apos;t lead to Spaghetti code on the wrong run um so you need to think about how your objects interacts so in the case of uh you&apos;re only reading the previous T instance it&apos;s fine because it&apos;s just a linear list so that&apos;s unctional so you don&apos;t have re issue so yeah in just say in this case you can just inside your um game provider which is still a family um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:50&lt;/strong&gt; · you you receive the ID Associated as parameter uh if you remember correctly so since you have the ID as parameter you could technically uh listen to the ID minus one to obtain the previous instance so you could like using the ref object you could do ref. watch uh game provider and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:10&lt;/strong&gt; · then pass your ID minus one and then you will be able to listen to the previous instance interesting I really like the fact that it&apos;s so easy to access all of this without having to worry about okay can I access this because am I above or below the provider like things you would be having with the provider package where you have to think about this you will still have an exception if you try to do something that is not a unit directional data flow so if you have so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:38&lt;/strong&gt; · if you&apos;re trying to read as a previous T and then the previous Tetris is trying to read the next Tetris you will have an exception telling you that it&apos;s not possible because yeah because it&apos;s cycle and it would be infinitely updating right yeah and then especially uh that&apos;s also why we can do some powerful things like the autod dispos flag because since it&apos;s an un data flow um you don&apos;t have this cyclic dependency so you know when&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:04&lt;/strong&gt; · you can dispose resources whereas if you had a cyclic dependency you wouldn&apos;t be able to know oh okay it&apos;s not used anymore um so so yeah rivot will not let you do very weird stuff you have to be careful and that&apos;s also that that&apos;s one of the biggest reason I&apos;m working on De on a de tool for rivot at the moment where where um I&apos;m extending the FL depal and um so you have a a new tab for rivert&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:35&lt;/strong&gt; · which shows like your typical State Management uh T tool where it shows um the um exp expanded view of an object but here also has an extra tab where you see the graph of States so um you would&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:53&lt;/strong&gt; · see oh this provider depends on this other provider and then you have this this tree um of states and see what what happened when you update this provider which or the provider depends on it and you also will likely see the widgets too at the same time so you could like I really like the idea yeah I like the idea I feel like I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:16&lt;/strong&gt; · feel like all the State Management Solutions could use a bit more love from the def tooling uh Direction because you have all this St manag and things but it&apos;s still kind of awkward to inspect the state uh especially during runtime and if like even if you are in debug mode you first have to set a break point you don&apos;t have to manually go through the variables and the references just having a way to have more information be&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:42&lt;/strong&gt; · visible with less effort I think that could be um very helpful and I think um I I&apos;ll give a shout out to Z go team like um tried the I&apos;ve already started working on the dev tool and I&apos;ve tried it um tried what&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:01&lt;/strong&gt; · existing in the dev tool and there actually have the possibility to expect objects D objects at R time so you are able to list all the all the properties and everything you want to implement a DA tool which means that I was able to um list everything we want to do and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:21&lt;/strong&gt; · um um in know typical death tool so list all your properties and list and potentially update them we can also um update the state of your um State at from the death to so you can click on something to go bu and update strings um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:41&lt;/strong&gt; · so yeah the death is very very very powerful and I think uh it&apos;s quite underrated at this point uh and maybe in the future more people will contribute to the death to once they realize that they can do all this fancy stuff that typical applications in dark would not be able to do mhm yeah that is indeed super interesting I didn&apos;t know you were able to do so much even during the runtime but that&apos;s definitely something I&apos;m super excited for so um yeah that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:10&lt;/strong&gt; · why I&apos;m surey if if anybody wants to know more just FL Remy um I&apos;m pretty sure once things are ready you&apos;ll be able to you you&apos;ll know um yeah on Z death to um I would&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:26&lt;/strong&gt; · say that I at this current stage I&apos;m able to obtain all the different informations needed to show the de tool like the graph and update the things and the only remaining thing is uh making the pretty because it&apos;s certainly not very pretty but I mean if you think about making stuff pretty um then I&apos;m pretty sure you&apos;re at a stage where all stuff is working so if it&apos;s just making it pretty I&apos;m pretty sure you&apos;ll be able to accomplish that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:59&lt;/strong&gt; · yeah so um I think we are slowly getting to the end do you have anything to add to say um yeah I would say if you have any feedback about repot whatever it is even if it&apos;s like oh no repot is super bad it&apos;s fine um please tell me I would love&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:21&lt;/strong&gt; · to get as much feedback as possible like the only thing I&apos;ve seen is people saying uh maybe thank you or something like this so it&apos;s great to hear but um I love to hear also about what problems people are facing so that maybe I can document it or maybe I have some new features to develop so if nobody is complaining it&apos;s difficult to do uh my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:45&lt;/strong&gt; · job you know I can&apos;t really improve this thing further if nobody&apos;s complaining because I don&apos;t know what to improve maybe it&apos;s just so good that there is nothing to improve who knows it&apos;s very unlikely I&apos;m pretty sure that there are many things I need to improve like for example I sometimes when I try&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:02&lt;/strong&gt; · when I randomly try stuff I find bugs so I&apos;m wondering is it that people don&apos;t bugs or is it that they didn&apos;t find it and I always have this question at the back of my mind do people actually use the stuff I make I never know I definitely&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:21&lt;/strong&gt; · do okay thank you so much for being on show um for all the listeners we&apos;ll have all the links we&apos;ll have his Twitter handle and the ways you can follow him in the show notes thanks again for being part of the show and thank you for good [Music] one&lt;/p&gt;
</content:encoded></item><item><title>Gemma now available in KerasNLP collection, Signal Input in developer preview, and more dev news!</title><link>https://rodydavis.com/videos/ai/gemma-now-available-in-kerasnlp-collection-signal-input-in-developer-preview-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/ai/gemma-now-available-in-kerasnlp-collection-signal-input-in-developer-preview-and-more-dev-news/</guid><description>Google&apos;s Developer News Show announces key updates including Angular signal inputs in preview, the availability of Gemma in the KerasNLP collection, MediaPipe support for LLM inference, and the debut of the Croissant metadata format.</description><pubDate>Tue, 12 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Gemma now available in KerasNLP collection, Signal Input in developer preview, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;k5DouVmByEQ&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 381  | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Intro
0:08 - Signal inputs now in preview for Angular devs! → https://goo.gle/4a7Pdv5
0:39 - Gemma is now available in the KerasNLP collection → https://goo.gle/43gi8uA&lt;br&gt;
1:02 - Croissant, a new metadata format for ML datasets → https://goo.gle/3PgtGbG
1:49 - MediaPipe now supports LLM inference and image generation → https://goo.gle/3v3y331&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody Davis.Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google for Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more The Developer Show → https://goo.gle/GDevShow
Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis;
Products Mentioned: Material Design - General, Angular - General;&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:03&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:04&lt;/strong&gt; · And this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:07&lt;/strong&gt; · Starting with Angular, signal inputs are now in developer preview.&lt;/p&gt;
&lt;h3&gt;Signal inputs now in preview for Angular devs!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · Signals provide a powerful reactivity model that enables you to efficiently monitor changes, derive values, while automatically notifying Angular when any specific part of your application needs to be rerendered.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:23&lt;/strong&gt; · Signal inputs can also improve your application by automatically marking OnPush components dirty, being more type safe, and can be derived and used in other signals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:32&lt;/strong&gt; · This can also be easily monitored when using effects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · You can learn more on the Angular blog.&lt;/p&gt;
&lt;h3&gt;Gemma is now available in the KerasNLP collection&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · Next, let&apos;s head over to Google AI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:41&lt;/strong&gt; · The Keras team is happy to announce that Gemma is now available in the KerasNLP collection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · Thanks to Keras 3, Gemma runs on Jax, PyTorch, and TensorFlow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:50&lt;/strong&gt; · Keras is also introducing two new features, a new LoRa API, or low-rank adaptation, and a large-scale model parallel training capability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · If you want to learn more, check out the Google for Developers blog.&lt;/p&gt;
&lt;h3&gt;Croissant, a new metadata format for ML datasets&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · If you&apos;re a machine learning practitioner looking to reuse existing data sets to train ML models, you&apos;ll probably spend a lot of time understanding data, making sense of its organization, and figuring out which subsets to use as features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · ML data sets cover a broad range of content types, including text and structured data to images and video.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21&lt;/strong&gt; · There are some general-purpose metadata formats for data sets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24&lt;/strong&gt; · But these were mostly designed for discovery.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26&lt;/strong&gt; · Today, we&apos;re introducing Croissant, which is a new metadata format ready for ML data sets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32&lt;/strong&gt; · It was developed collaboratively by a community of industry and academia as part of the ML Commons effort.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · Starting off, it will support three popular collections of ML data sets-- Kaggle, HuggingFace, and OpenML.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:43&lt;/strong&gt; · To learn more about the 1.0 release of Croissant, check out the Google research blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:48&lt;/strong&gt; · And finally, MediaPipe now supports two new generative AI tasks, including LLM inference and image generation.&lt;/p&gt;
&lt;h3&gt;MediaPipe now supports LLM inference and image generation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · The LLM inference API supports Gemma 2B and can run on-device generative AI text-to-text generation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01&lt;/strong&gt; · You can learn more about MediaPipe on developers.google.com.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:05&lt;/strong&gt; · To learn more about all these week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:10&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:13&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:14&lt;/strong&gt; · Thanks for watching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · And we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:17&lt;/strong&gt; · [MUSIC NOTE]&lt;/p&gt;
</content:encoded></item><item><title>Google Computer Use model, Gemini CLI extensions, and more! - Google Developer News October 2025</title><link>https://rodydavis.com/videos/ai/google-computer-use-model-gemini-cli-extensions-and-more-google-developer-news-october-2025/</link><guid isPermaLink="true">https://rodydavis.com/videos/ai/google-computer-use-model-gemini-cli-extensions-and-more-google-developer-news-october-2025/</guid><description>In the October 2025 Google Developer News, speakers review the Google Computer Use model, Gemini CLI Extensions, and the Agent Development Kit, alongside details on upcoming hackathons and DevFest 2025.</description><pubDate>Thu, 23 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Google Computer Use model, Gemini CLI extensions, and more! - Google Developer News October 2025&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;BWX8cnMTq7E&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Welcome to this episode of Google Developer News – the October Edition. This month we dive into new features in the AI realm that include the Google Computer Use model, Gemini CLI Extensions, and the Agent Development Kit (ADK). Want to get involved? We also explore upcoming hackathons—AI Accelerate, the Chrome Built-in AI Challenge, and the Cloud Run Hackathon, while preparing for DevFest 2025. Register for these events below.&lt;/p&gt;
&lt;p&gt;Chapters:
0:00 - Introduction
0:12 - Google Computer Use Model
0:39 - Gemini CLI Extensions
1:18 - Jules Tools
1:53 - Agent Development Kit
2:56 - Hackathon opportunities
3:02 - AI Accelerate Hackathon
3:40 - Chrome Built-in AI Challenge
4:21 - Cloud Run Hackathon
5:00 - DevFest 2025
5:43 - Wrap up&lt;/p&gt;
&lt;p&gt;Resources:
Introducing the Gemini 2.5 Computer Use model → https://goo.gle/4o6qzm5
Now open for building: Introducing Gemini CLI extensions → https://goo.gle/47Cb3bI
Meet Jules Tools: A Command Line Companion for Google’s Async Coding Agent → https://goo.gle/47DVArC
Delight users by combining ADK Agents with Fancy Frontends using AG-UI  → https://goo.gle/4qp2mc4
AI Accelerate Hackathon → https://goo.gle/49gBx3G
Built in AI Challenge → https://goo.gle/4hude4C
Cloud Runathon → https://goo.gle/48W0dya&lt;/p&gt;
&lt;p&gt;Watch more Google Developer News → https://goo.gle/4e8Rysd&lt;br&gt;
Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#GoogleDeveloperNews&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned: Gemini, Agent Developer Kit (ADK)&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Welcome back to the Google Developer News Show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:02&lt;/strong&gt; · I&apos;m your host, Rody Davis, and we&apos;ve got lots of exciting news to get through this month.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:07&lt;/strong&gt; · Let&apos;s get into it.&lt;/p&gt;
&lt;h3&gt;Google Computer Use Model&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:12&lt;/strong&gt; · Firstly, we released the Google Computer Use model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · It&apos;s based on Gemini&apos;s best-in-class visual understanding and reasoning capabilities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:19&lt;/strong&gt; · But what does that mean?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · It gives AI agents the ability to fill out forms, manipulate dropdowns and filters, and operate behind logins.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:27&lt;/strong&gt; · It&apos;s a crucial step in building powerful general-purpose agents that can automate all types of complex tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · Try it out now in the Gemini API on Google AI Studio and Vertex AI.&lt;/p&gt;
&lt;h3&gt;Gemini CLI Extensions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · We&apos;re also introducing a major new tool for developers, Gemini CLI extensions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · They&apos;re designed to be a significant evolution beyond the core functionality of MCP, giving developers more functionality and a seamless experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:53&lt;/strong&gt; · They&apos;re easily installed and they include a simple playbook, so Gemini knows how to execute the job that&apos;s customized for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00&lt;/strong&gt; · Gemini CLI extensions also put you in control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03&lt;/strong&gt; · You can combine extensions, chain commands, and build a personalized tool chain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · Open up new possibilities!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · Whether it&apos;s streamlining a personal workflow or integrating a company&apos;s internal tools, we can&apos;t wait to see what you&apos;ll build.&lt;/p&gt;
&lt;h3&gt;Jules Tools&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:18&lt;/strong&gt; · Next up, say hello to Jules, our asynchronous coding agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · Jules is designed to understand the full context of your project and take on tasks such as writing tests, building new features, fixing bugs and a lot more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32&lt;/strong&gt; · You can run multiple tasks at the same time, and they&apos;ll finish asynchronously.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · Until now, you&apos;ve only been able to work with Jules in your web browser, but with Jules&apos; tools and new lightweight command line interface, you can create tasks and monitor what Jules is up to, all without leaving your workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:52&lt;/strong&gt; · The last year has seen incredible advances in agenic AI, but a powerful agent is only half the story.&lt;/p&gt;
&lt;h3&gt;Agent Development Kit&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:58&lt;/strong&gt; · How do you create a truly collaborative experience between your user and your AI?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:03&lt;/strong&gt; · To help with that, we&apos;re excited to tell you Agent Development Kit, or ADK, now works with the AG-UI Open Protocol.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:10&lt;/strong&gt; · But what does this mean?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:12&lt;/strong&gt; · You can now build sophisticated AI agents and give them a collaborative front-end, all with minimal effort.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:18&lt;/strong&gt; · The integration provides access to a new class of features right out of the box, which will transform the user experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:25&lt;/strong&gt; · Your agent can go beyond text in generating and rendering UI components directly in the chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:31&lt;/strong&gt; · For added security, users can supervise, approve or correct agent action before they&apos;re executed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:37&lt;/strong&gt; · You can even empower your agent to directly interact with the front end, allowing it to fill out forms or navigate pages for the user.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:45&lt;/strong&gt; · With the ADK and AG-UI integration, you can focus on what you do best: shipping powerful, intelligent and collaborative AI applications.&lt;/p&gt;
&lt;h3&gt;Hackathon opportunities&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:56&lt;/strong&gt; · Now for hackathons: we have three to highlight this month, where you can showcase your skills to the world.&lt;/p&gt;
&lt;h3&gt;AI Accelerate Hackathon&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · First up is AI Accelerate: Unlocking new frontiers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:05&lt;/strong&gt; · It&apos;s a little different to the other hackathons, with developers choosing to take on challenges posed by our partners at Elastic and Fivetran.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:13&lt;/strong&gt; · Create a solution that will show how Elastic plus Google Cloud can transform how people and data interact.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:20&lt;/strong&gt; · Or combine Google Cloud with Fivetran to create an application that improves customer experiences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:26&lt;/strong&gt; · Two challenges, one unique opportunity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:29&lt;/strong&gt; · The winners of each challenge will win a share of $50,000.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:33&lt;/strong&gt; · Visit AI-accelerate.devpost.com to find out more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:39&lt;/strong&gt; · Next, do you have what it takes to build the best AI-powered web app or Chrome extension?&lt;/p&gt;
&lt;h3&gt;Chrome Built-in AI Challenge&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:44&lt;/strong&gt; · Join the Google Chrome Built-in AI Challenge 2025 for the chance to win a share of $70,000.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:51&lt;/strong&gt; · There&apos;s a wide range of tools at your disposal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:54&lt;/strong&gt; · Create using a suite of built-in AI APIs and extend your project&apos;s reach by implementing a hybrid AI solution with Firebase or Gemini API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:05&lt;/strong&gt; · It&apos;s a great opportunity to unleash your creativity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:09&lt;/strong&gt; · To enter, visit googlechromeAI2025.devpost.com and make sure you submit your project before the deadline on October 31st, at 11:45 p.m. Pacific time.&lt;/p&gt;
&lt;h3&gt;Cloud Run Hackathon&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:21&lt;/strong&gt; · The final hackathon for this month is the Google Cloud Run Hackathon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:25&lt;/strong&gt; · Build and deploy serverless applications on Cloud Run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:29&lt;/strong&gt; · We&apos;re looking for projects that really showcase the power, simplicity and scalability of serverless technology.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:35&lt;/strong&gt; · Your solution can fall into either the AI Studio, AI agents or GPU category, as long as they&apos;re deployed on Cloud Run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:44&lt;/strong&gt; · The choice is yours.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:45&lt;/strong&gt; · As well as the grand prize winner, there will also be winning projects in each of the three categories.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:51&lt;/strong&gt; · To enter, visit run.devpost.com and submit your project before the deadline, November 10th, at 5:00 p.m. Pacific time.&lt;/p&gt;
&lt;h3&gt;DevFest 2025&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:00&lt;/strong&gt; · We end this month&apos;s Google Developer News Show with a big one, DevFest 2025.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:06&lt;/strong&gt; · DevFest is the largest community-led developer conference in the world.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:10&lt;/strong&gt; · Last year, Devfest hosted over 560 community-led conferences globally, reaching over 810,000 developers in 105 countries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:21&lt;/strong&gt; · DevFest is a chance for developers, from aspiring to senior professionals, to come together to share what they know and learn from each other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:29&lt;/strong&gt; · We hope you&apos;ll join us to learn, connect and grow with fellow developers, no matter where you are in the world.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:35&lt;/strong&gt; · Visit developers.google.com/community/DevFest to find a DevFest event near you.&lt;/p&gt;
&lt;h3&gt;Wrap up&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:43&lt;/strong&gt; · From powerful new agents to hackathons and DevFest, we hope our October updates spark new ideas and help you build something amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:51&lt;/strong&gt; · I&apos;m Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:52&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you soon.&lt;/p&gt;
</content:encoded></item><item><title>How to Automate PR Summaries with Opal AI</title><link>https://rodydavis.com/videos/ai/how-to-automate-pr-summaries-with-opal-ai/</link><guid isPermaLink="true">https://rodydavis.com/videos/ai/how-to-automate-pr-summaries-with-opal-ai/</guid><description>Learn how to use Google&apos;s Opal AI to build Git-Clarity, a no-code mini-app that automatically generates clear, concise summaries of GitHub pull requests.</description><pubDate>Mon, 12 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;How to Automate PR Summaries with Opal AI&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;gphcuJu8iHo&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Uncover how Opal from Google Labs revolutionizes building AI systems with its intuitive natural language and visual workflow editor. This tutorial guides you through creating an AI-powered mini-app, Git-Clarity, designed to summarize GitHub pull requests efficiently. Understand Opal’s multi-step workflow creation, fast iteration capabilities, and instant hosting features, making AI development accessible for everyone.&lt;/p&gt;
&lt;p&gt;Resources:
Learn more → https://goo.gle/3Yx4aDq&lt;/p&gt;
&lt;p&gt;Chapters:
0:00 - Introduction to Opal from Google Labs
1:06 - Opal’s intelligent multi-step workflows
2:10 - Exploring the generated workflow steps
3:29 - Verifying the complete workflow
4:52 - Iteration in practice: Refining output
5:25 - Sharing your live prototype
5:35 - Closing thoughts and future possibilities with Opal&lt;/p&gt;
&lt;p&gt;Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned:  Google AI&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction to Opal from Google Labs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:02&lt;/strong&gt; · Opel is a new experiment from Google Labs that you can use for building and testing chained AI systems in minutes, all using natural language and visual workflows. In this video, we&apos;re going to be using Opel to build a fully working mini app called Git Clarity, an AI powered tool that turns any GitHub pull request into a clear one paragraph summary for non-technical stakeholders.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:24&lt;/strong&gt; · We&apos;ll build this entire thing from a blank Opal canvas using only natural language prompts and Opel&apos;s visual workflow editor. There&apos;s no coding required here. Instead, you describe what you want and iterate visually. Opel is designed for maximum versatility.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · Opel is a sandbox for rapid AI prototyping. Perfect for anyone who wants to chain models, test ideas, iterate on prompts, and experiment with tool setups before moving to production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:51&lt;/strong&gt; · Today we&apos;re going to show a developer focused use case. How Opel lets us go from I want an app that summarizes PRs to a working UI, a chained AI workflow and a sharable tool in just a few minutes. If you&apos;re new to Opel, here&apos;s a mental model to help you throughout this demo. Opel intelligently creates multi-step workflows automatically adding steps like fetching web content, processing data, and displaying results.&lt;/p&gt;
&lt;h3&gt;Opal’s intelligent multi-step workflows&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · In the visual workflow editor, every step is a component. Inputs, generate steps, and outputs are all visible along with their connections. This allows for fast iteration. We can change prompts, adjust steps, and rerun immediately using natural language. And with instant hosting and sharing, your app is deployed automatically and becomes sharable via a URL. We&apos;ll also get into the iteration process with Opel because prototyping isn&apos;t about getting it perfect the first time. It&apos;s about exploring and trying out ideas quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47&lt;/strong&gt; · Let&apos;s get started. Here we&apos;re starting with a blank Opal canvas where we&apos;re going to build the entire app using natural language. First, we enter our prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01&lt;/strong&gt; · Opel immediately interprets this and creates a workflow-based app. Not just a simple form, but a complete multi-step workflow. Check out how Opel automatically generates a title in an input field while intelligently creating workflow steps to fetch and process the [music] PR content. Okay, this is a pretty good workflow. Let&apos;s take a look.&lt;/p&gt;
&lt;h3&gt;Exploring the generated workflow steps&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:20&lt;/strong&gt; · So far, we got a user input step for the PR link, a fetch PR content step that uses the Git web page tool to retrieve the PR, a generate step to summarize the content, and an output step to display the results. Notice how each step can use different tools like maps and [music] web search. Let&apos;s click on the summarize PR step to see its configuration. When you select a step, Opel opens up a configuration panel on the right side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:49&lt;/strong&gt; · Here you can see the model dropdown.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:52&lt;/strong&gt; · Opel makes it easy to choose from available models for each step.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:57&lt;/strong&gt; · You can also see the current prompt in the editing area below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · First, in advanced settings, we&apos;ll manually add some context about how we want the model to behave. You can edit the prompt directly, or you can use Opel&apos;s natural language editing to help update the prompt for you. We&apos;ll update it to say this. Opel has already created the output display for us as part of the automatic [music] workflow generation. This is useful to preview the model output while keeping in mind the model may provide different responses to the same prompt over time.&lt;/p&gt;
&lt;h3&gt;Verifying the complete workflow&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:30&lt;/strong&gt; · We&apos;ll now verify if the workflow is complete. In the editor view, we can see the GitHub PR link connected to get PR content which connects to summarize PR which connects to generate PR summary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:43&lt;/strong&gt; · All the connections are safely made.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:45&lt;/strong&gt; · Opel&apos;s visual workflow editor shows us the complete flow. If we wanted to enhance this workflow, we could easily add more steps or tools. For example, we could upload reference documents from Google Drive using the add assets feature or add additional processing steps. But for now, our workflow is complete and ready to test.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:07&lt;/strong&gt; · Time to test our app with a real GitHub pull request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:11&lt;/strong&gt; · If you click the app view, you&apos;ll see the immersive app experience. For now, we&apos;ll preview the app in the side panel in our editor view. In our output, we see the get clarity title and a start button. I&apos;ll click on it to begin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:25&lt;/strong&gt; · Let&apos;s use a PR from Google&apos;s now in Android repository. Paste the link in the input field and click submit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:33&lt;/strong&gt; · Watch as Opel runs through our chained AI workflow, fetching the PR content from the web, processing it with AI to generate a summary and displaying the result. [music] In seconds, we have a clear non-technical summary of the pull request. Exactly what we built this app to do. Nice. One of Opel&apos;s core [music] strengths is how easy it is to iterate and test changes. So, let&apos;s refine the summary prompt to improve the output. Go back to the editor view to use the suggest and edit box to refine the generate step.&lt;/p&gt;
&lt;h3&gt;Iteration in practice: Refining output&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:03&lt;/strong&gt; · Opel updates the prompt immediately. No need to redeploy or restart anything. We can test again right away to see the improved output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:14&lt;/strong&gt; · This is the power of rapid prototyping with Opel. Iterate quickly, test immediately and refine your chained AI workflows in real time. When you&apos;re happy with your prototype, you can click the share button.&lt;/p&gt;
&lt;h3&gt;Sharing your live prototype&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:27&lt;/strong&gt; · Opel generates a public URL that you can use to share with your team. And here it is live. You can think of Opel as a place where you explore, experiment, and validate AI workflow ideas before committing to production builds. In just a few minutes, we built Git Clarity entirely through natural language and visual editing, a chain system that fetches web content, processes it with AI, and displays the results.&lt;/p&gt;
&lt;h3&gt;Closing thoughts and future possibilities with Opal&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:49&lt;/strong&gt; · Beyond Git Clarity, you could use Opel to prototype customer support bots, internal dashboards, creative tools, data processing pipelines, or any chained AI workflow you can imagine. The sky is the limit. Try Opel out at Opel.google and start building your own mini apps today.&lt;/p&gt;
</content:encoded></item><item><title>How to connect interfaces to the cloud with AI agents</title><link>https://rodydavis.com/videos/ai/how-to-connect-interfaces-to-the-cloud-with-ai-agents/</link><guid isPermaLink="true">https://rodydavis.com/videos/ai/how-to-connect-interfaces-to-the-cloud-with-ai-agents/</guid><description>This guide demonstrates how to use the Agent Development Kit (ADK) to connect local Flutter interfaces to the cloud, empowering AI agents with massive reasoning models, real-time APIs, and complex workflow orchestration.</description><pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;How to connect interfaces to the cloud with AI agents&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;Dum-LYLm0Uw&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Verdure example → https://goo.gle/3XY7GX8
GenUI package → https://goo.gle/4oXUoou&lt;/p&gt;
&lt;p&gt;Give AI agents massive reasoning models, real-time APIs, and the ability to perform complex tasks. Watch along as Googler Rody Davis leverages Agent Development Kit (ADK) to securely access databases, call internal APIs, and orchestrate complex workflows. Watch along and discover the power of GenUI SDK for Flutter.&lt;/p&gt;
&lt;p&gt;Chapters:
0:00 - Intro
0:36 - What is Agent Development Kit?
1:17 - Rendering the UI for app workflows
1:56 -[Demo] Verdure
2:44 - [Demo] Building a new “Budget Slider”
4:32 - Summary&lt;/p&gt;
&lt;p&gt;Watch more Flutter Flight Plans → https://www.youtube.com/watch?v=pMoUg3dkDJk&amp;amp;list=PLjxrf2q8roU1yXu4k7ivSLAa0cizD4feH
🔔 Subscribe to Flutter → https://goo.gle/FlutterYT&lt;/p&gt;
&lt;p&gt;#Flutter&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned: Agent Development Kit, GenUI SDK for Flutter&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:07&lt;/strong&gt; · [music] In a recent video, get started with GenUI and Flutter, we explored how to build dynamic client side UIs. We focused on local interaction, showing you how to create adaptive experiences without needing a complex server setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:24&lt;/strong&gt; · Today, we&apos;re taking the next step. We&apos;re looking at how you can connect those interfaces to the cloud, giving your agents access to massive reasoning models, real-time APIs, and the ability to perform complex tasks. To do that, we can use agent development kit or ADK. It provides the infrastructure needed to build these powerful back-end agents.&lt;/p&gt;
&lt;h3&gt;What is Agent Development Kit?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · ADK is an open- source framework that handles the heavy lifting, reasoning, state management, and tool execution. to communicate with the user. The agents built with ADK use a standardized streaming protocol which we can use to have a standard way to send dynamic UI components to the front end. Decoupling your agents logic from the client implementation. The real power lies in what ADK enables on the server. Your agents can securely access databases, call internal APIs, and orchestrate complex workflows, things you can&apos;t easily do on a client device.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · But how do we render the UI for these workflows?&lt;/p&gt;
&lt;h3&gt;Rendering the UI for app workflows&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · Instead of sending static images or HTML, the agent generates structured JSON payloads describing the intent of the UI. This is where Flutter Genui package comes in. Your app receives the structured response which might include an initial generated plan alongside specific inputs for refinement. It maps the schema to the widgets you defined.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · The agent provides the content and the controls and your app decides to render this as a seamless experience. This means your agent can think, fetch data, and update the UI incrementally without you having to write complex socket management code. Let&apos;s see this in action. This is for a demo of a landscape design agent. It is a Flutter client powered by an ADK backend. Let&apos;s say the user starts a new landscape design. The agent processes a request and responds not just with the text, but with a structured JSON payload describing the interactive interface.&lt;/p&gt;
&lt;h3&gt;[Demo] Verdure&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · It&apos;s similar to how a web server might return a form, but here it&apos;s fully native. But unlike a normal client server form, it can generate unique questions based on the content that the user uploaded powered by Gemini. In this video, the agent composed a layout in its response and the Flutter app composed to gather the corresponding widgets. When the user takes action, it will update the client side data model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:37&lt;/strong&gt; · And when they hit submit as a user action, it will be sent back to the agent with the new context and returns the new state.&lt;/p&gt;
&lt;h3&gt;[Demo] Building a new “Budget Slider”&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:45&lt;/strong&gt; · This looks like magic, but the code is surprisingly simple. Let&apos;s build a new budget slider component in the Verdue sample to see what is needed on the client and server to have these custom components beyond what the default catalog provides. First, in Flutter, we can introduce the default widget catalog. Then, we can use the Genui package to map a name and a schema with a widget builder to the new custom component. Notice that we aren&apos;t writing any networking code here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:10&lt;/strong&gt; · We just tell the SDK if the agent asks for a budget slider, verify that the data matches the schema and render the standard Flutter slider. Now, let&apos;s flip to the backend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:21&lt;/strong&gt; · Since the Genui architecture relies on a standard JSON protocol to describe the widgets and intent, your backend can technically be anything. A Dart server, a Node.js app, or even a Python service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:33&lt;/strong&gt; · As long as it returns the correct JSON schema, the Flutter client can render it. However, to build a robust, scalable agent, we recommend using ADK. It handles the complex reasoning and state management for you. In our code, we simply define agents logic to have it return the UI structure. ADK takes care of the rest, bridging the intelligence back to your Flutter app. But why go to all this trouble? Why not just render markdown or HTML? It comes down to control. With Genui, you are not limited to just generic text and images. You define components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:05&lt;/strong&gt; · This means the agent can leverage custom interactive components like the budget slider that fit perfectly into your app&apos;s design system. You get the flexibility of generative UI without sacrificing the native look and feel and performance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:20&lt;/strong&gt; · Because we&apos;re sending lightweight JSON structures, not pixels or heavy layout code. The payloads are tiny. And because the widgets are in Flutter, you get 60 FPS animations and native gestures. By combining the Flutter Genui package with an ADK backend, you can unlock a whole new class of applications. Check out the Verdue sample and start building your own widget registry at github.com/flutter/genui.&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:44&lt;/strong&gt; · We cannot wait to see what you&apos;ll build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:47&lt;/strong&gt; · [music] [music]&lt;/p&gt;
</content:encoded></item><item><title>Leverage Gemini in your Android apps, Global Gamer Challenge, and more dev news!</title><link>https://rodydavis.com/videos/ai/leverage-gemini-in-your-android-apps-global-gamer-challenge-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/ai/leverage-gemini-in-your-android-apps-global-gamer-challenge-and-more-dev-news/</guid><description>The Google Developer News Show announces how to leverage Gemini in Android apps, details the Global Gamers Challenge, and discusses the Solution Challenge 2024.</description><pubDate>Tue, 16 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Leverage Gemini in your Android apps, Global Gamer Challenge, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;2HrFs0Lfb-w&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 373  | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Intro
0:09 - Leverage Gemini in your Android apps → https://goo.gle/4aZCnjN
0:51 - Announcing the Global Gamers Challenge → https://goo.gle/4aVHAZZ
1:23 - Solution Challenge 2024 → https://goo.gle/4b2gbpp&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody Davis. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google for Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow
Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;Products Mentioned: Gemini Nano&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned: Google AI, Gemini&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:03&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:04&lt;/strong&gt; · And this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:08&lt;/strong&gt; · Have you integrated AI into your Android apps yet?&lt;/p&gt;
&lt;h3&gt;Leverage Gemini in your Android apps&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · If not, now is the perfect time to leverage Gemini in Android.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · Android can run a local model called Gemini Nano on select devices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:18&lt;/strong&gt; · But to target all Android models, you can use Gemini Pro, which runs off device in Google data centers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:24&lt;/strong&gt; · Gemini can be called via the Gemini API and Google AI SDK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:29&lt;/strong&gt; · To get started with prompts and testing various interactions, you can use the new Google AI Studio to generate API keys, code snippets, and easily go from idea to app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · Lastly, you can import the fresh generative AI sample in Android Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · Make sure to leave a comment and let us know what you&apos;re building with Gemini Pro.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · To learn more, check out the Android Developers blog link below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:50&lt;/strong&gt; · We are excited to release the new Global Gamers Challenge for Flutter.&lt;/p&gt;
&lt;h3&gt;Announcing the Global Gamers Challenge&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · This is an eight-week contest to design, build, and publish sustainable games sponsored by Flutter and Global Citizen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00&lt;/strong&gt; · The awards will be judged based on the following criteria-- originality and creativity, sustainable action and story, use of animation, and effective multi-platform development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11&lt;/strong&gt; · The contest winners will receive a trip to New York City in September and meet some of the Flutter team for a day of workshops and mentorship, and then celebrate with 60,000 global citizens at the Global Citizen Festival in 2024.&lt;/p&gt;
&lt;h3&gt;Solution Challenge 2024&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:24&lt;/strong&gt; · To learn more, check out flutter.dev/global-gamers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29&lt;/strong&gt; · If you&apos;re a student developer, then check out the new Solution Challenge 2024 for using technology to address UN sustainable development goals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · This year marks the five-year anniversary of the Google Developer Student Clubs Solution challenge, and we&apos;re excited to be back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:43&lt;/strong&gt; · There are some great prizes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45&lt;/strong&gt; · And the first hundred winners will be announced in April.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47&lt;/strong&gt; · Make sure to submit your project by February 22 at the link below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:52&lt;/strong&gt; · To learn more about all this week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:59&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · Thanks for watching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01&lt;/strong&gt; · And we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>The Gemini Cookbook, Popover API lands in Baseline, and more dev news!</title><link>https://rodydavis.com/videos/ai/the-gemini-cookbook-popover-api-lands-in-baseline-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/ai/the-gemini-cookbook-popover-api-lands-in-baseline-and-more-dev-news/</guid><description>Google&apos;s weekly developer news update covers major announcements, including the Popover API landing in Baseline, new tools for CI/CD compliance, ML for build repair, and insights on the Gemini Cookbook.</description><pubDate>Mon, 29 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;The Gemini Cookbook, Popover API lands in Baseline, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;oRjt_mIm6IY&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 388  | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Intro
0:19 - Popover API lands in Baseline → https://goo.gle/4a3qs2K
0:44 - Achieving privacy compliance with your CI/CD → https://goo.gle/4a6n0Vi
1:24 - Safely repairing  broken builds with ML&lt;br&gt;
1:54 - Improving Gboard language models via private federated analytics
2:37 - Gemini Cookbook → https://goo.gle/44jp9vq&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody Davis.Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google for Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more The Developer Show → https://goo.gle/GDevShow
Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis;
Products Mentioned: ‎Gemini, Web Stories - General;&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi, my name is Rody, and I&apos;m a developer relations engineer on the developer solutions team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:08&lt;/strong&gt; · This is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · Make sure to check the description for any links or topics mentioned.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:15&lt;/strong&gt; · Now let&apos;s get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:17&lt;/strong&gt; · Starting with the web, the Popover API lands in baseline and in all major browsers, which means it&apos;s safe to use in your applications and websites.&lt;/p&gt;
&lt;h3&gt;Popover API lands in Baseline&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:25&lt;/strong&gt; · If you&apos;re unfamiliar with the API, it allows you to promote content to the top layer, add light-dismiss, default focus management with tabbing, and finally, accessible keyboard and component bindings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · The Popover API makes it possible to create complete interactions that previously required JavaScript.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:41&lt;/strong&gt; · You can learn more on the web.dev blog.&lt;/p&gt;
&lt;h3&gt;Achieving privacy compliance with your CI/CD&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · Next, we have two stories to help you with your continuous integration and delivery pipelines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:49&lt;/strong&gt; · First, on the Google Developers blog, you can read about how to achieve privacy compliance using checks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · This integration into CI/CD allows you to move towards automating compliance and privacy standards as part of the release cycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · There are multiple benefits when adopting checks into your application release pipeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06&lt;/strong&gt; · This includes real-time intelligent alerting, understanding data sharing for third-party SDKs, making sure you follow company policies, keeping your Google Play Data Safety section up-to-date, and deploying quickly with confidence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · You can learn more on the developers.google.com blog.&lt;/p&gt;
&lt;h3&gt;Safely repairing broken builds with ML&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:24&lt;/strong&gt; · From Google Research, you can now learn how machine learning can be used to safely repair broken builds as part of a CI/CD pipeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31&lt;/strong&gt; · The DIDACT ML model is used to predict build fixes and repair them automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · This model, which ran as an experiment for 11 weeks, resulted in 2% reduction in active coding time and developer time after a change was submitted to review as well as a 2% increase in total change throughput throughout the week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:51&lt;/strong&gt; · You can learn more on the research.google blog.&lt;/p&gt;
&lt;h3&gt;Improving Gboard language models via private federated analytics&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · Next, heading over to AI, we can learn about how Gboard has been improving its language models via private federated analytics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01&lt;/strong&gt; · Gboard uses language models to improve typing through features like next word prediction, autocorrection, Smart Compose, slide to type, and proofread.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:10&lt;/strong&gt; · To boost Google&apos;s keyboard performance while keeping the user data private, we have worked with language experts to refine its dictionaries, developed novel privacy-preserving techniques based on federated analytics and differential privacy to discover out of vocab words directly on the user&apos;s devices, and employed secure hardware infrastructure for confidential and externally-verifiable server-side data processing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · You can learn more about the approaches taken on the research.google blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:36&lt;/strong&gt; · And finally, you can learn about some of the new exciting additions to the Google Gemini Cookbook.&lt;/p&gt;
&lt;h3&gt;Gemini Cookbook&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · This GitHub repo contains lots of examples that can be run on Colab, targeting Python developers, or anyone wanting to work with Gemini via REST.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:49&lt;/strong&gt; · Some of the exciting quickstarts include content for the following-- system instructions, function calling, embeddings with RAG or Retrieval Augmented Generation, uploading files like audio and PDFs through the Files API, finetuning, JSON mode, streaming, and safety.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:04&lt;/strong&gt; · There are so many more examples not listed, but make sure to check out the GitHub repo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:07&lt;/strong&gt; · You can open any of them in Google&apos;s Colab and grab an API key from Google AI Studio to get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:13&lt;/strong&gt; · You can learn more at github.com/google gemini/cookbook.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · To learn more about all these week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:23&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:26&lt;/strong&gt; · I&apos;m Rody for the Developer Show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:27&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:29&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Offline vector search with SQLite and EmbeddingGemma</title><link>https://rodydavis.com/videos/ai/offline-vector-search-with-sqlite-and-embeddinggemma/</link><guid isPermaLink="true">https://rodydavis.com/videos/ai/offline-vector-search-with-sqlite-and-embeddinggemma/</guid><description>Learn how to build an offline Retrieval-Augmented Generation (RAG) system that uses SQLite and EmbeddingGemma to query and embed documents entirely in the browser, even without internet connectivity.</description><pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Offline vector search with SQLite and EmbeddingGemma&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;sobPBztQTtU&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Learn from Rody Davis, Senior Developer Relations Engineer at Google, how to query and embed documents using SQLite and embeddings with EmbeddingGemma and Gemma3. Create an offline RAG system that runs in the browser offline.&lt;/p&gt;
&lt;p&gt;Resources:
Github → https://goo.gle/4p2b3b1&lt;/p&gt;
&lt;p&gt;See more Web AI talks →  https://goo.gle/web-ai&lt;br&gt;
Subscribe to Chrome for Developers → https://goo.gle/ChromeDevs&lt;/p&gt;
&lt;p&gt;Event: Web AI Summit 2025&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned: AI for the web, Gemma 3&lt;/p&gt;
&lt;p&gt;#ChromeforDevelopers #WebAI&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:01&lt;/strong&gt; · [music] How&apos;s it going? My name is Rodie and I am a developer relations engineer at Google working on the AI workflows team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · Super excited to be here today at the WebAI summit. Something I&apos;m very passionate about is ondevice and local first types of applications. And so before we get started, let&apos;s talk about vectors and databases. Vectors, as you know, can be generated um with both hosted and local models. There&apos;s a lot of trade-offs that go for each one of those and typically uh will require specific needs of your application. So, vector stores can grow quite large and often require an API to access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:43&lt;/strong&gt; · And while that&apos;s fine for certain types of applications, uh it may not be ideal when you have intermittent network connectivity, for example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · Uh one important thing about vectors uh if you&apos;re not familiar with them is you have to have the same encoder and decoder uh both wherever you use to query and to update documents. And this is really important because you can&apos;t take advantage of like a really powerful um encoder and then a very lightweight decoder. You have to use the same um which was kind of frustrating when I uh was first getting into it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · And then another thing that uh server side can really take advantage of is they can be so much faster because they have a lot of RAM. They&apos;re optimized for NVME storage. But I&apos;ve listed a lot of pros on the server side, but why would you even want them on the client? Well, first of all, you can store the vectors just for the user. You never have to worry about running a query and getting some sort of uh dimensionality for uh content that&apos;s not theirs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47&lt;/strong&gt; · You can also just have the advantage of it already being partitioned for that user on the client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:55&lt;/strong&gt; · So with most things that require trade-offs, usually a hybrid approach is more appropriate. And in this case, we can use the server side to have some nice parallel compute to be able to batch encode a bunch of vectors. And we can store them inside of Firebase using Fire Store vector support which was added.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · And of course there&apos;s vector databases that already work um a lot better for vectors, but one of the nice things about fire store is it gives us a nice syncing uh modality that we can use on the client that we can store everything in a bucket per user. Um and really it&apos;s meant to be a fallback when the model isn&apos;t downloaded. I&apos;m a huge fan of SQLite and one of the cool things about SQLite is you can load extensions including vector support. So we can actually pull in those vectors from fire store into SQLite and then you can query them directly on the client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · But here&apos;s where the magic really starts to happen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:50&lt;/strong&gt; · When you go to update models, you can use that local encoder and decoder to incrementally regenerate new documents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:59&lt;/strong&gt; · This makes it really nice to pull down a massive data set and then as the user is making changes and edits, you get to keep that up to date without having to do that round trip and requiring internet always.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:10&lt;/strong&gt; · So, embedding Gemma is a super awesome encoder and decoder uh that we have launched and I really love it. It&apos;s about 38 million parameters. It&apos;s meant to be run on mobile devices but just because you can do that doesn&apos;t mean you can&apos;t use it on the server which is really awesome including support for things like cloudr run where we make it really easy to launch it on uh with Olama. So you can have a nice fallback API when the model isn&apos;t downloaded yet and you just want to have this kind of ad hoc experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:41&lt;/strong&gt; · One of the reasons I like using it is it has 768 dimensions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:47&lt;/strong&gt; · So it has a very significant uh quality for the types of uh tasks that you can throw at it. It&apos;s still configurable and uh the just the whole Gemma family is really awesome. Uh but well I know a lot of people today have talked about Jimma 3N and you can totally use that with this but for this talk it&apos;s just going to be on the the database side and vector support uh without LLMs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:12&lt;/strong&gt; · Another cool thing about uh these models is you can use transformers.js which was uh talked about many times today. It allows us to use the CPU and GPU to run inference on these uh encoder models and it also supports the 768 dimension space that embedding Gemma can use and output for the vectors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:33&lt;/strong&gt; · Here&apos;s a uh code snippet on how you would get this running with embedding Gemma. I&apos;m using the uh Onyx runtime for the embedding Gemma version of it, the 300 uh million parameter option. And here we can just create a simple pipeline that uses feature extraction as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:50&lt;/strong&gt; · well as being able to take that embeder give it the correct task type which it can be query or document or others listed on the documentation and then we just kind of uh normalize the vectors before we return it back and since we&apos;re on the web it&apos;s important to return it as a float 32 array because that&apos;s what SQLite&apos;s also going to expect um for the storage as well as fire store.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:14&lt;/strong&gt; · So like I said, fire store supports vectors which is awesome. It makes it really easy to sync. Uh when fire store will first load into your application, it&apos;ll pull down the documents that you have queried for that user. And as you make updates, fire store takes care of all of the work of if you update a single document on the server side, it will just pull down the incremental uh patches as well as making updates can send it back up to the server. So you don&apos;t have to manage any complex sync logic on your side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:41&lt;/strong&gt; · But they also launched vector support which means you can literally add the vector type directly into those documents keeping it collocated with that user and their collections.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:54&lt;/strong&gt; · So here&apos;s just a simple snippet of how you might do that in fire store uh using the the modular JavaScript SDK. You can just create a fire store application using the uh the app that you initialize and in this case it&apos;s an emoji application and you have the embedding which you can then add the doc and then use the vector type which you can import as well from the SDK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:17&lt;/strong&gt; · So SQLite huge fan uh there&apos;s a really cool project called SQLite VEC if you&apos;re not familiar with it I definitely suggest you give it a look. uh it allows us to use low-level KN&amp;amp;N queries uh directly inside of SQLite by extending the syntax. Uh this project has also expanded a lot since the first version.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:37&lt;/strong&gt; · It now has metadata filtering, partitioning and virtual columns and so much more. But this allows us to create those embeddings directly into SQLite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:47&lt;/strong&gt; · Now, you can also store the the blobs of the float 32 directly inside of um regular tables, but one of the cool things about the virtual tables is it&apos;s optimized for those queries. So, it doesn&apos;t have to scan do a full table scan every time you uh do a query. Also, SQLite compiles to Wom and you can add any extensions that you have inside of that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:12&lt;/strong&gt; · So, um, in this example that I&apos;m going to share on GitHub later, uh, it has SQLite vec pre-installed, but you can totally add your custom ones as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:22&lt;/strong&gt; · So, here&apos;s an example of how you might do that in SQLite. Uh, we&apos;re importing the official uh, SQLite package here uh, from SQLite.org as well as just pulling down the Wom module. You can just create it like another table using the VEX0ero uh, table syntax. And this allows us to have that float 768 dimension syntax.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:43&lt;/strong&gt; · And you would obviously change this for the type of uh encoder and decoder you&apos;re using. But that&apos;s it. You just work with it like a normal SQLite database if you&apos;re familiar with that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:53&lt;/strong&gt; · But this is all happening on the client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:55&lt;/strong&gt; · It can do massive data sets. It&apos;s often that you can run millions of queries in just like a second on uh on the browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:02&lt;/strong&gt; · So definitely suggest giving it a look.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:05&lt;/strong&gt; · So when it comes to querying, it&apos;s also very similar to SQL. I know this may not be familiar for everyone, but uh as a a mobile developer and someone who likes to build applications, writing SQL queries on the client, knowing it&apos;s just the data set makes it really easy to create the types of views that I want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:22&lt;/strong&gt; · And in this case, I just query from the emojis embedding table. I join on that foreign key and then here&apos;s where the magic comes in with the match keyword, which is using the ve0 functions as well as the KN&amp;amp;N queries with the limit. and then we can order it by the distance and then uh grab it out and present it to the user later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:43&lt;/strong&gt; · So, time for a demo. This is a uh a little bit different take than the other demo from earlier which was about using embedding Gemma for um emojis. I want to create a better vector search for emoji emojis. So, I took the entire Unicode data set and I vectorized each of the descriptions with the emojis. So as you&apos;re typing it returns the emojis that are closest to that embedding space based on what your query is and each time you on key press it will actually vectorize the query itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:14&lt;/strong&gt; · So this model once it gets downloaded onto the browser this can happen completely offline. So you can obviously expand this to other applications where you can have uh documents that you pull in for your business data or just specific uh types of tool calls like for example you can vectorize a thousand tool definitions and only provide maybe five to the model at any given time. It really opens up and expands the types of use cases that you can build. Um this code is available uh on my GitHub.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:46&lt;/strong&gt; · You can check it out at emojis search. Uh I am uh usually pretty available on GitHub and Twitter and LinkedIn. So uh definitely feel free to reach out. But uh thanks so much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:01&lt;/strong&gt; · [music]&lt;/p&gt;
</content:encoded></item><item><title>Flutter &amp; Antigravity | Observable Flutter #76</title><link>https://rodydavis.com/videos/antigravity/flutter-antigravity-observable-flutter-76/</link><guid isPermaLink="true">https://rodydavis.com/videos/antigravity/flutter-antigravity-observable-flutter-76/</guid><description>Experts discuss Flutter development and functionality within Google&apos;s new AI-powered IDE, Antigravity, during Episode #76 of Observable Flutter.</description><pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Flutter &amp;amp; Antigravity | Observable Flutter #76&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;s5uGpolyHd4&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Watch as Craig Labenz is joined by Rody Davis and Andy Zhang to explore Flutter development in Google&apos;s new IDE, Antigravity.&lt;/p&gt;
&lt;p&gt;Download Antigravity → https://antigravity.google
GitHub → https://goo.gle/4nBfIQj&lt;/p&gt;
&lt;p&gt;Watch more Observable Flutter → https://goo.gle/ObservableFlutter
Subscribe to Flutter → http://goo.gle/FlutterYT&lt;/p&gt;
&lt;p&gt;#Flutter #ObservableFlutter&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [music] Hello everyone and welcome to another episode of observable flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:13&lt;/strong&gt; · My name is Craig Labenz and I&apos;m your host and today we are treading into uncommon rare ground in observable flutter history where I have the pleasure of being joined by not just one but two esteemed guests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · One most of you probably already know and the other you will know in short order.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · So as you may have heard there was some news recently. Google has a new AI based editor and it&apos;s called anti-gravity. I&apos;ve of course been playing around with it a lot. Maybe some of you have as well and today we&apos;re going to kick the tires and I I&apos;m very excited about the guests today a lot of expertise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:57&lt;/strong&gt; · They&apos;ve both either used or been working on gravity quite a bit. So before we get too far into it please do recall that this is the flutter community. We hold ourselves to a high standard and I know today we will meet and maybe even exceed that standard.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · My guests today are both Roddy Davis and Andy Zang. They work at Google of course. Roddy is one of my compatriots in the developer relations wing of Google and Andy is a design engineer on anti-gravity and has been building it for I don&apos;t actually know how long.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · We&apos;ll let Andy tell us that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39&lt;/strong&gt; · So Andy and Roddy welcome to observable flutter. You can introduce yourselves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46&lt;/strong&gt; · Hey guys. Great to Great to be here. My name is Andy. I&apos;m a design engineer on the anti-gravity team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53&lt;/strong&gt; · And great question earlier. I&apos;ve been working on anti-gravity for just over 3 months now. So it&apos;s been a very very exciting crazy 3 months and we&apos;re super happy to put this out in the wild for everybody to be using it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:06&lt;/strong&gt; · I&apos;ll pass it on to Roddy now. Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:08&lt;/strong&gt; · Awesome. I&apos;m Roddy. I&apos;m on the AI workflows team at Google. I&apos;m in DevRel and I&apos;m super excited to be able to think about all the ways that we&apos;re using Gemini that includes Gemini CLI, anti-gravity and a bunch of other services that you have come to love.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:24&lt;/strong&gt; · I also really love working with the flutter team and all the different full stack projects across Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · Nice. Yeah. Okay. So it&apos;s funny you know the the answer of 3 months is is quite funny. It&apos;s really not a very long amount of time but I think as a lot of people know this all came out of the Windsor fact position which was just kind of mid summer of this year. So it couldn&apos;t have been much longer than that and uh so obviously these projects take a lot longer than 3 months but Google has has scrapped this together very quickly very honestly impressively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:01&lt;/strong&gt; · I didn&apos;t know what to expect the first time I launched anti-gravity and just a few nights ago I was coding with it of all night. I was preparing to open source this library that I&apos;ve been working on and I found it to be extraordinarily delightful and helpful in a way that really exceeded my expectations and you know I&apos;m I&apos;m not just saying that like that was my my very real experience using it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:27&lt;/strong&gt; · So Roddy recently you put up you you just kind of screen recorded something put up a quick video on YouTube.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:35&lt;/strong&gt; · Folks may or may not have seen that. Can you walk us through what you were doing and just kind of how you think about using anti-gravity?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:43&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:44&lt;/strong&gt; · So anti-gravity is like a really cool way to start from scratch or kind of work at multiple levels at once. Um as people know like when you&apos;re working with flutter it&apos;s really common to have a bunch of like mono like a mono repo with like sub packages and specifically anti-gravity has a thing called an async manager and you can actually open up individual packages. It&apos;s like their own like sub projects and be doing a bunch of tasks across everything at once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:14&lt;/strong&gt; · Um the video that I uploaded was like starting from scratch with flutter and like that end to end experience of like I have this idea for an app. Can you come up with a plan iterating on the plan and having it like do native dependencies, fix cocoapods for you and also even run Mac OS in the background and it was really cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:36&lt;/strong&gt; · I I had a great experience with it and when you can get it working with it under like 10 minutes I feel like that&apos;s usually a good sign of success. So Fixing cocoapods too. That is the real challenge. [laughter] No small feat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:53&lt;/strong&gt; · That&apos;s flutter development&apos;s version of you know does it run on IE6? Yes. Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:58&lt;/strong&gt; · Windows or from web development like 10 15 years ago. Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:04&lt;/strong&gt; · Interesting comment here. So I&apos;ll obviously just go to chat randomly. Kyle says hoping we can get rid of Gemini 3 is going to start working suddenly and wonder how powerful it is but it&apos;s completely broken in anti-gravity. I presume you&apos;re basically talking about the the quota issues. Gemini 3 has been in demand and I it&apos;s been really fun I think to see how many people all around the world are using it but uh you know there are for more servers. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:30&lt;/strong&gt; · [laughter] GPUs and they&apos;re they&apos;re trying to slap more into those data centers as fast as they can. Uh all right. So at some point today we&apos;re going to I&apos;m going to launch anti-gravity and we&apos;re going to we&apos;re going to play around with it and actually try to build something and if we&apos;ve obviously got Andy here to make sure we don&apos;t miss any of the bells or whistles that might be less obvious.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:51&lt;/strong&gt; · Either of you two have any other thoughts before we get into that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:56&lt;/strong&gt; · Um I would say this is like a really cool opportunity for people that are looking for a desktop driven agentive experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:05&lt;/strong&gt; · If you think that it&apos;s just a VS code fork you know you definitely wouldn&apos;t be remiss because like it does share the same extensions and other stuff. But it really does like have so many little DX features that have added up to like bring a really delightful experience when working in this IDE. Like for example the ability to like kick off like background sub processes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:28&lt;/strong&gt; · You&apos;re in the terminal and I can highlight a part of the terminal and then have that put into the chat or being able to like debug things like in all the different levels. When you run a web server it opens up a notification that can open up the browser and control it, take screenshots and take it&apos;s like this this full end to end experience that I always uh kind of wish that VS code could do but I just feel like it does it at just a really nice level and then things like the async manager and really deep Gemini integration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:01&lt;/strong&gt; · Yeah. I think Roddy couldn&apos;t have said it better on that. I think we&apos;ve really spent a lot of time trying to think of exactly about the things you were just talking about which is the end to end experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:09&lt;/strong&gt; · There&apos;s a lot of work that happens in the editor but there&apos;s also so much work that happens inside of the browser and there&apos;s so much work that happens in the research and why have such a fragmented experience across like these different services when you can all have it bundled in one and I think that cohesion of all of it in bundled in one really makes the developer experience just way more delightful and I think that folks are going to find that in anti-gravity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:31&lt;/strong&gt; · Yeah. Like I I literally caught myself copying logs. I was like wait like this is now a built in feature. I no longer have to do that. I can just say look at this terminal tab and then just grab it for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:41&lt;/strong&gt; · [laughter] Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:43&lt;/strong&gt; · I think a big part of Oh go for it actually. I was just going to say I I actually don&apos;t think this is why I was really excited to have you specifically on Andy and I I imagine Roddy also knows a lot of these things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:54&lt;/strong&gt; · Mhm. I think right now I am the opposite of an anti-gravity power user. I have not done any of these native terminal like just piping the text through from one thing to the next features that you two are talking about. So anyway I&apos;m I&apos;m excited to see these in action.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:10&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:12&lt;/strong&gt; · What were you going to say Andy? Yeah. I was going to quickly add before we we jump on to the demos that I think the the really cool thing about building anti-gravity as one of the design engineers on it is that like you use the tool that you are building and so to that flow of like you know like you have these terminal logs like oh I got to like move it around. Like there was at some point someone on the team was like this is frustrating like why don&apos;t I just go ahead and build this so that it&apos;s just way easier. And so when you have an entire team of people who run into these issues and they are actually in control of the product.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:45&lt;/strong&gt; · That&apos;s how you you end up with the situation where the product it feels like as if everyone has really thought through all these things that users kind of go through and so hopefully that&apos;s what folks experience when they try it out. Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:57&lt;/strong&gt; · [clears throat] Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:58&lt;/strong&gt; · So that begs the question. There was a developer who was working on anti-gravity and they were tired of piping you know manually copying and pasting error logs and then they built the solution to it in the anti-gravity. So are you all building anti-gravity in anti-gravity? Exactly. It&apos;s it&apos;s anti-gravity inception.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:17&lt;/strong&gt; · [laughter] Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:20&lt;/strong&gt; · Best way to dog food. That&apos;s for sure. Hell yeah. Yeah. Yeah. Exactly. All right. Well I&apos;m ready if you two are ready. So [clears throat] I don&apos;t have a tremendously detailed plan today for what we&apos;re going to build. Long time viewers will know that I often just ask chat for inspiration on what we want to build but I&apos;m also happy to take suggestions from either you Andy or Roddy but yeah let&apos;s let&apos;s begin here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:48&lt;/strong&gt; · So I&apos;m going to share my screen which is right here. And here we go. I&apos;ll make the text a lot bigger. Now, my first question for both of you, and you can answer in whatever order, or as is tradition, just talk over each other. I am not I would say I&apos;m not natively drawn to, or an expert in, or feel very comfortable in the agent first workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:22&lt;/strong&gt; · So, how do you And maybe I&apos;ll start with you, Andy. How and you are you imagining developers who want to really experience the agent first workflow use Antigravity? Like, where should I click? What screens am I on? How do I think about it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:42&lt;/strong&gt; · Yeah, I think that&apos;s a really good question. And I think, you know, a lot of developers were just fluent in code, and that&apos;s kind of what we live and breathe, because that is ultimately the source of truth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:52&lt;/strong&gt; · But as folks shift towards more of a like an agent first view, it really takes a bit of time, you know, you&apos;re seeing a lot of unknowns, and you&apos;re like, I haven&apos;t seen this before, and I don&apos;t know if this if this is for me. And as you use it, some people might love it, other people might still prefer the code. And ultimately, what we try to bake in is for you to have the agency to pick, like, I actually prefer working in code, so I&apos;m going to stay within the editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:15&lt;/strong&gt; · But if you want to work in something more agent first, we do have a the the agent manager, which you where your cursor currently is. Um and in that view, the focal point is a lot more on the agent. And so, suddenly you&apos;re able to like focus specifically, like, oh, like, this is what I should be doing next, which is starting a new conversation in this this current workspace Story Forge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:42&lt;/strong&gt; · And then from there, the agent will kind of carry you along the journey of like what it&apos;s kind of working through.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:47&lt;/strong&gt; · And at any point when you realize, like, hey, this might not be for me, I prefer go like stay in the code, you still have the open editor button on top right, or over there, right below the input, to bring you back into a more familiar experience in the editor. So, it&apos;s really just like you get to choose your own adventure, depending on what you want to do. If you want to read code, the editor is there for you. But if you want to read what the agent is doing and thinking for you, then the agent manager, or the side panel within the editor, is the right fit for your use case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:17&lt;/strong&gt; · So, ultimately, the flexibility is built in for users to dip their toes in the agent first experience, but also always hop out when they want to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:26&lt;/strong&gt; · So, I think, one of the interesting ways to think about it, if you&apos;re like really deep in the Dart and Flutter world, is this is like build runner, but with a prompt, and you can have multiple sub processes going at the same time. Uh you know, if you&apos;re doing a lot of Flutter work, it&apos;s probably common that you&apos;re going to have a bunch of uh folders cloned on your your development machine, and you&apos;re going to have a bunch of probably internal packages, external packages, uh and at&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:56&lt;/strong&gt; · any given time, you may be wanting to make sure the change log is up to date, and you&apos;re bumping a version, or you&apos;re over here making sure that I&apos;ve updated all the dependencies. There&apos;s there&apos;s a lot of like common tasks that in Flutter and Dart that just have toil that either have commands run um that aren&apos;t necessarily complicated, but this is where like an async manager can be really nice, cuz you can you can have all of your your packages that you&apos;ve cloned uh like as like top-level things with subtasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:27&lt;/strong&gt; · And you can be saying, like, okay, go update this, and then you can go work on your project, and then you can come back, and you&apos;ll see this is waiting for input from the user, so I can see, yep, oh, that looks good, go ahead and publish, and then you can kind of be working around. It kind of gives you that ability to just like hyperthreading to be able to switch processes faster while keeping your keeping yourself in the zone a lot more of the time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:55&lt;/strong&gt; · Yeah, so one one question I have in response to all of that, and it it kind of I guess it&apos;s in in response to one of the first things that Andy was saying. Is it true, like if I actually haven&apos;t even tested this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:07&lt;/strong&gt; · This is a genuine question. Is it true that if I start some conversation here, like, you know, please I don&apos;t even know if I already did this or not, but please update this to the latest Flutter and Dart versions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:23&lt;/strong&gt; · [clears throat] So, we&apos;ll see I&apos;ll just let this go. Look, it may already the task may be complete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:28&lt;/strong&gt; · The difference between the agent view and the typical editor view, is it really just that the code is hidden?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:34&lt;/strong&gt; · Like, if I click editor view right now, I&apos;ll see this same conversation happening. Yes, but it&apos;s also in another window. I think that&apos;s what confused me. Think about it as like a menu for your sub projects. So, it&apos;s like it lives above the the folder level. Like, this is like across anything that&apos;s inside of Antigravity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:58&lt;/strong&gt; · So, it&apos;s like your front door, basically. Like, you can you can have things in 20 different directories all open in the async manager. They don&apos;t have to be all in like the same sub project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:08&lt;/strong&gt; · Yeah, I think that&apos;s a really good way to think about it is that you ultimately become like more zoomed out, and that&apos;s also the reason why we call it the manager, because you&apos;re just not just focus on one folder anymore. You can zoom out and see many different things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:20&lt;/strong&gt; · And that just gives um so much option for you to just like spin up like as many as you want across as many workspaces as you want, and just feels a lot more powerful. I see. Okay. All right. All right. Yeah, the I was originally thrown by the jump in the jump to another window here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:41&lt;/strong&gt; · Oh, we&apos;re going to beta. All right, let&apos;s do it. Let&apos;s jump to beta. And the quick way to do it is command E. And you can Okay. kind of jump out into it. Yeah. Oh. Is that toggling windows, or is it staying within the same window? It should be opening it up as a new window. Yeah, cuz it&apos;s its own experience. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:05&lt;/strong&gt; · Okay. I think I have some uh some more tire kicking to do there. One thing that I expected I got used to in the Gemini CLI, whenever it wants to run a command, you&apos;ve got the three options, yes this time, yes always, and you know, no, what are you thinking?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:24&lt;/strong&gt; · There doesn&apos;t Is there a yes always? Cuz it feels like when it wants when it comes back for input, I&apos;m not seeing a way to say like, yes, and you don&apos;t need to ask. Uh Andy, you want to talk about some of the settings that are available?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:42&lt;/strong&gt; · Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:43&lt;/strong&gt; · Andy&apos;s connection is not what Andy hoped it would be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:49&lt;/strong&gt; · [laughter] Yeah. He&apos;ll be back at some point. Brody, you want to talk about Yeah, go ahead. So, basically, if you click on we should have an Antigravity user settings that is in the gear icon on the top.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:04&lt;/strong&gt; · Basically, command comma, I&apos;m guessing you&apos;re Yeah, and so here is actually where you can choose when to trigger those terminal. You can say like at what level the agent deciding if it&apos;s going to request permission is the first option in that drop-down. And you can say like always proceed. And and if you keep scrolling down, this is like where you can really start to fine-tune it for your exact preferences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:29&lt;/strong&gt; · Um you can even like disable you know, allowing anything at all. You can auto-execute always. You can even like say like, I&apos;m going to explicitly allow Flutter and NPM as things that you never have to request. And so, this is like kind of that space where you can customize cuz everybody&apos;s going to have a different level of um um Oh, I see. So, I could say like, you know, Dart is always allowed. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:58&lt;/strong&gt; · Oh, is it a comma-separated list here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:59&lt;/strong&gt; · But never use like never run RM or something. Yeah. Right. And that&apos;s exactly what I was going to say. Yeah. [laughter] RM is off limits. Yeah. Uh okay, also I would make this bigger for everyone, but command plus and minus don&apos;t work on this window. So, apologies, folks, for the small text. So, I have not read all of the settings here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:18&lt;/strong&gt; · And it to avoid just doing that, you know, reading aloud for the class, are there any on this screen, Brody, you just called attention to the allow list and deny list for commands. Are there any others on this screen that we should think about specifically?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:36&lt;/strong&gt; · Maybe not this one, but on the left side, you&apos;ll notice that there are tabs over here, and these are good ways for folks to really configure not just the agent experience, but the entire Antigravity experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:48&lt;/strong&gt; · One of the core features that is probably one of my personal favorites now when I&apos;m doing web development is the browser, and how it&apos;s able to go ahead and make changes for you. So, Oh, is is my connection No, it&apos;s from a few minutes ago. You&apos;re perfect now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:03&lt;/strong&gt; · Okay, great. Yeah, yeah. I just connected Ethernet, so apologies for being disconnected, but I should hopefully be good now. Um Hardwired will do it. Yeah, lesson learned. On the browser side, over here, as you can see, like, there&apos;s a really great integration with the browser with Antigravity, and you should be able to like configure it over here. There&apos;s also the execution policy for JavaScript, and also if you want to enable and disable it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:32&lt;/strong&gt; · Um obviously, there&apos;s also configurations with the editor, given that it is a VS Code fork, so folks can change the settings with the editor however they like. Um and then notifications in tab as well. The notifications are great. It&apos;s nice to be able to like literally be on a different program, and just like it notifies you that you have something to review. That opens up the async manager and I can jump right back in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:58&lt;/strong&gt; · Mhm. Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:00&lt;/strong&gt; · So, as an example of the Nice. Yeah. It&apos;s really, really nice if you just have a bunch of things running and you know you want the agent to like intervene and like tell you if it&apos;s block it doesn&apos;t want you don&apos;t want it to RM something. So, it&apos;s going to be like &amp;quot;Hey Craig, like you want me to RM or this or not?&amp;quot; You&apos;re like &amp;quot;Thank god you asked me. You go ahead and [laughter] you just like do not RM my root directory.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:26&lt;/strong&gt; · Or any other directory probably.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:27&lt;/strong&gt; · removing all bugs on deleting the project. So. Yeah, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:30&lt;/strong&gt; · [laughter] Yep, can&apos;t have bugs if you don&apos;t have code. Uh great question here from Kyle for the browser you are allow list. Is this for something like pinning documentation? Like what what uh how should I think about this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:40&lt;/strong&gt; · Yeah, that&apos;s a great question. So, basically um the browser integration the way it works is that it&apos;s going to open up uh Chrome in a different profile. Um this profile is different from your usual Chrome that you open uh and it has like a set of white list that you might allow um when you when you first uh when you first onboard. Um but sometimes when it&apos;s on a new website uh it will it will always request for permissions if you want to add it to your allow list or not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:08&lt;/strong&gt; · And that way um you have confidence that it&apos;s not just going to go rogue and like start modifying a bunch of websites that you didn&apos;t give it permission in, but instead it will always make sure that you consent to uh it having control on a specific website before it does anything such as uh scrolling through the page, clicking on it, uh typing out input and a lot of typical browser actions that uh we typically do as users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:34&lt;/strong&gt; · And I this would also be to help uh hedge against like the LLM is taking control and going to be taking screenshots. So, if it uh tries to click on links that are like maybe malicious or uh what you&apos;re not intending on, this is kind of like keeps that little sandbox uh for you know known websites.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:52&lt;/strong&gt; · Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:53&lt;/strong&gt; · Uh interesting. So, I mean, hopefully one doesn&apos;t have malicious links in their own projects. Otherwise, something very strange is going on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:02&lt;/strong&gt; · Like in in a website that you&apos;re crawling or something. Yeah. I see. So, that the browser there is not only for it to test your web-based app, it is also for to crawl the web and do random things cuz obviously Gemini CLI and if you go to gemini.google.com and other LLMs as well will look up stuff for you, but they do it in a headless way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:28&lt;/strong&gt; · Am I hearing that this will also kind of do some research on your behalf and you can watch it research as it crawls different websites. Is that what&apos;s going on here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:38&lt;/strong&gt; · Um that is not the primary use case for it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:42&lt;/strong&gt; · Uh but if you ask it to, it has the ability to do that. Um the the agent obviously has the ability to decide like how it wants to proceed. And so, to your point like if there is more efficient approach like the headless approach or hitting an API endpoint, it&apos;s going to go ahead and do that as opposed to opening up a browser, waiting for load time, loading entire like DOM, and then actually clicking on things. And so, um it will it knows the options available and it will always find the most efficient ways of doing things based off of your preferences. Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:11&lt;/strong&gt; · [clears throat] Okay, so anyway, this looks basically like a you know SSH hosts file or or [snorts] something where it&apos;s just it&apos;s a one one domain per line, I&apos;m guessing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:21&lt;/strong&gt; · Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:23&lt;/strong&gt; · Um and localhost is there because the primary use case um that that I I personally love is specifically for live coding when you are trying to spin up a a website and you are um you&apos;re just ripping it you&apos;re letting the agent just go crazy and um build a ton of features across many of your different conversations. And then um the browser is able to just start testing it for you uh and as after it&apos;s done testing it, it&apos;s also going to be able to record what it did while it was testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:50&lt;/strong&gt; · And so, you could just be in the agent manager and be like &amp;quot;Oh, like can you just show me your work?&amp;quot; And it shows you the video recording of the thing that it built and also uh how it tested out whether it&apos;s a scrolling or like clicking on different buttons. And when all of that happens, it&apos;s like &amp;quot;Oh, like not only did it write all the code for me, but it also has proof that the website works as expected.&amp;quot; Um which saves a lot of work on our end and gives a lot of trust in the code that was written.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:17&lt;/strong&gt; · Um maybe something Andy uh could you talk about like just the level of control it has over the browser cuz I think a lot of people think it may just like kind of open it up and do it kind of basic stuff, but I&apos;ve been really impressed with how powerful it is. I I&apos;d love to see that as well. Maybe we can throw together Let me Let me just actually let&apos;s I&apos;m going to start a new project. It&apos;ll just be Flutter counter. We&apos;ll run it in Flutter web. And then let&apos;s see it uh driving that. I think that&apos;ll be pretty fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:46&lt;/strong&gt; · So, I&apos;m going to go to I don&apos;t think this code is going to be interesting. I have a repo where I put code that I write on this show and I don&apos;t think uh yet another Flutter counter app needs to be committed. So, this could be an example of using the async manager if you want. Uh if you want to start yeah. Do command E. Whoop, that&apos;s a bigger than I was expecting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:12&lt;/strong&gt; · [laughter] So, the reason that happened, I kept pressing plus Oh, plus plus plus minus minus plus plus plus plus plus on the settings tab. And it wasn&apos;t rendering there, but it was doing something and it was waiting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:24&lt;/strong&gt; · [laughter] Oh, that&apos;s funny. Okay, so uh Roddy Yeah, so the async manager. Yeah, go ahead and hit uh command E. And um I believe it would work in the playground, although um I would suggest like uh we could just start a new conversation at the top.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:43&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:44&lt;/strong&gt; · And then this is where you can say I want to create a Flutter app, use the Flutter create tool, uh blah blah blah. Now, what&apos;s I don&apos;t want to be in this Yeah, I kind of want to just be like nowhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:56&lt;/strong&gt; · Um so, I don&apos;t want to be in this other workspace. No, that it should be um uh start a new conversation should be the folder, but you can um Yeah, so apparently I I have a folder that doesn&apos;t exist, but now it seem I don&apos;t know. Let&apos;s just see what this does. Oh, yeah, this is this should work. Yeah. Okay, yeah. I thought the button, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:18&lt;/strong&gt; · Which only targets web, use the empty template. Let&apos;s see if it a femoral file system, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:28&lt;/strong&gt; · Oh, I may need to create this folder?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:32&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:34&lt;/strong&gt; · Um I think you should be good. Press okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:38&lt;/strong&gt; · Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:41&lt;/strong&gt; · Well, given that there apparently wasn&apos;t a problem, that was a surprising pop-up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:46&lt;/strong&gt; · So, uh you&apos;ve mentioned the async manager. Is the async there just that the task is going to take however long and it comes back when it&apos;s done?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:55&lt;/strong&gt; · Uh it&apos;s just really a manager, but um it&apos;s optimized for seeing all of your async tasks and what the latest state is. I wonder if it is blocked on this folder not existing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:05&lt;/strong&gt; · Yeah, maybe maybe what we could try doing is we can open up a uh another new directory uh and then opening up that as a workspace. Um you can do this within the agent manager as well. Okay. Uh so, like here I would just say open a GUI in some new directory. Uh no, so so open up a new workspace um from the sidebar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:29&lt;/strong&gt; · Um so, if you press on the taco button at the top, um you&apos;ll see that there&apos;s an open workspace below. There you go. And this allows you to add another workspace um within um your your file explorer. And so, here you can just designate another one. Yeah. Uh and this is when you can really just elevate your experience in Antigravity to be working across many, many different workspaces. And this is a lot obviously a view that you just don&apos;t get when you are inside of uh the the editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:00&lt;/strong&gt; · So, would a workspace like I have this little folder here uh called Flutter DevRel FDR, not to be confused with the president. And I&apos;ve put a ton of scratch projects in here. Is this the kind of folder that would make sense to be a workspace or should I be one more folder in?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:17&lt;/strong&gt; · Workspace should be like yeah, the individual one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:20&lt;/strong&gt; · Yeah. Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:21&lt;/strong&gt; · Okay, so I need to make a new folder here and this would be like Yeah. Uh AGUI web test and that&apos;s my workspace. Yep, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:30&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:31&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:31&lt;/strong&gt; · And keep in mind you could have like a folder open from downloads, another one open from your developer folder, another one in documents, and they&apos;re all like inside the async manager.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:40&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:41&lt;/strong&gt; · So, create a new Flutter web project which or Flutter project which only targets web. And then what did I say? Use the empty um flag. Or template, I said. I&apos;ve also been really impressed with uh specifically how powerful Gemini 3 is to like uh take on these like really long-running tasks like uh whereas I had to clear the chat a lot with 2.5, like I&apos;ve been able to go a lot longer uh with three.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:11&lt;/strong&gt; · Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:13&lt;/strong&gt; · So, I&apos;m seeing a lot of questions in the chat about MCP. It&apos;s about 50% people saying we need the Dart and Flutter MCP server and the other 50% are comforting them and assuring them that it already exists. Yep, you can install Dart and Flutter, yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:29&lt;/strong&gt; · Yeah, so as a a an Antigravity user, how do I prove to myself which MCP servers I have installed?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:37&lt;/strong&gt; · The way that you could do this currently uh is if you open up the editor um and then click on that three-dot uh drop-down on the right side right below right next to the X. The three dots. And then MCP servers. Uh click on your uh yes, so here you can download some of the MCP servers, but if you want to see exactly which ones you have installed, you can click on manage MCP servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:06&lt;/strong&gt; · Um and then you can also view your raw config uh at the top. Uh right there you go. Aha. Yeah. Great. Nice. It&apos;s funny. I&apos;ve installed MCP server in VS Code and for Gemini CLI before and on my personal computer, but it this one didn&apos;t have it installed, but now I do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:26&lt;/strong&gt; · So this was actually useful because I apparently didn&apos;t even have it installed. Yeah. Yep. And you can also in Flutter VS Code extensions, which you&apos;d probably already have too. But I believe when you first launch Anti-Gravity, you can import those from VS Code as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:42&lt;/strong&gt; · Yeah, I wonder if I failed to do that or if it got reset. I&apos;m not sure. Anyway, great. That was a great little side quest and it actually paid dividends for me as well, which I wasn&apos;t even expecting. Now, let&apos;s return to our previous side quest, which was to see for ourselves with our own eyes, as Darth Vader would say, the web the embedded web browser and AGY driving it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:06&lt;/strong&gt; · So I presume that I would just start by saying what should I tell should I ask Gemini to run You could say like run the run the web app to see if it&apos;s rendering correctly or something like that, maybe. That should get it to do a screenshot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:27&lt;/strong&gt; · Uh if you go to the editor Yeah, go back to the editor. I can help you out there. Open the editor toggle agent and click on back to agent. There you go. Back to agent. That&apos;s what I needed. Yeah, yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:40&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:41&lt;/strong&gt; · So you can run the app using that. Okay, great. So I&apos;ll say run it, please. 30% 33% waste of tokens there for manners. It&apos;s also super super fast. I&apos;ve been really excited with how much it can do in parallel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:00&lt;/strong&gt; · And I like that another like just small little detail, but like when you&apos;re doing a big task and you have some additional feedback to give it, you just add a new message and it&apos;ll add it to the queue and bring it and kind of modify it. It doesn&apos;t have to stop everything. And the implementation plan, you can actually comment on individual pieces of it. So you can say actually like use this package instead or I don&apos;t want to use that and this all takes it as feedback while it&apos;s live uh um updating. So Oh my gosh, this is so funny.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:31&lt;/strong&gt; · I have no ability to think ahead at all. I said use the empty thing but I actually wanted to have it drive the counter app. So a terrible time to remember that I typically want to use dash dash empty. That&apos;s all right. So it could probably add the counter app pretty easily, but uh Yeah, I was just going to say let&apos;s um add some interactive UI to this app. I don&apos;t care what it does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:58&lt;/strong&gt; · Before you uh before you uh oh, one thing that you could try doing is uh I think you&apos;re currently on on uh on fast mode. Um if you toggle that into planning mode, um we could try out some of the things that Roddy was talking about. Um so a quick&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:16&lt;/strong&gt; · quick explanation of that is planning mode is where you want the agent to be able to uh think in more like task blocks and being able to do its own planning work uh and this is one to give the agent more the of the ability to create these artifacts, which are really great summaries of like different checkpoints. So as an example, uh what Roddy was mentioning was like uh the agent is able to create implementation plan for you. So that way before it starts modifying any type of code, it tells you like this is how I&apos;m thinking about building this interactive UI that you currently have in mind.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:48&lt;/strong&gt; · Um or if it did a bunch of work and it also worked with the browser, the agent is also able to create a walk-through for you and basically tell you, &amp;quot;Hey, I I did all this work for you. This is my proof of work. Here&apos;s some screenshots of the browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:06&lt;/strong&gt; · Um what else do you want me to do?&amp;quot; Uh and so that&apos;s really like the the the kind of capabilities that planning is able to do. Whereas if you just want to do something very very quick and dirty um and you don&apos;t want the agent to do all this like planning type of work, that&apos;s where the fast mode uh comes in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:24&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:26&lt;/strong&gt; · And you can just have it say continue too if it gets stuck.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:34&lt;/strong&gt; · Yeah, I I&apos;ve also really liked the uh the the task list that it generates too when when it&apos;s already starting on the implementation. I find that the model just stays on track a lot better and can be doing this in kind of individual parallel subtasks and uh yeah, it&apos;s just been super So one thing I&apos;m I&apos;m maybe concluding here, I don&apos;t think planning mode is maybe that great with uh just like, you know, run the app, please. It&apos;s like making a a plan for how to run the app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:02&lt;/strong&gt; · Yeah, so [laughter] yeah, yeah, yeah, yeah. I wouldn&apos;t use it for that, but that&apos;s that&apos;s pretty funny.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:10&lt;/strong&gt; · So you could say like a good thing for like planning mode would be like I want a um I want to use routing and have this type of feature first folder structure and I want to have these models and blah blah and like it would like plan that all out as like a kind of mini PRD and then create subtasks to like go and like implement that. But the cool part is you can comment and be like &amp;quot;Oh, instead of go router, use this one instead or use this for state management&amp;quot; and stuff like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:39&lt;/strong&gt; · Um so you can kind of like guide the model what your preferences are and then uh hopefully save those as rules files for the future. Got it. Yeah, okay. So I&apos;m I definitely want to do exactly that. I just can&apos;t help it continue to appreciate how funny this is. The app has been launched for like 3 minutes and it finally decided that it did it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:00&lt;/strong&gt; · [laughter] Checked it off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:04&lt;/strong&gt; · That&apos;s just very funny. Yeah, well well done there. You deserve a cookie there. You&apos;re the best boy, Gemini. All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:12&lt;/strong&gt; · So I think it would be fun to I I want to see to take a big step back since Gemini CLI arrived and even Gemini Code Assist before that, periodically on this live stream I&apos;ve tried to lean really heavily into AI for a specific project and even had Randall, one of our Flutter GDEs on twice to work&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:36&lt;/strong&gt; · through this concept of having the LLM start with a PRD and whatnot and at that point it was really a roll-your-own experience to you know, you could just say like, &amp;quot;Man, burp up all this code for me, please.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:52&lt;/strong&gt; · Or you could be more thoughtful and and start with a plan and then turn that into a PRD and implementation steps and whatnot. Sounds like this experience of doing that is now basically just baked into selecting planning out of the drop-down here. So I do want to see that work and kind of see what the UI looks like, although we&apos;re getting a bit of a preview here. I&apos;ll close this for now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:15&lt;/strong&gt; · So I was thinking maybe we could ask this to um build on this and maybe we use state management to like store the history of colors and have a back button to kind of go back through history and see what random colors we had before. All right, not a very something more complicated like add undo redo for individual screens and then be able to use routing to store a kind of a list of the those pages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:47&lt;/strong&gt; · Yeah, that that that&apos;s basically um you know, undo would be go back, redo would be go forward in the stack. Yeah, we&apos;re totally totally thinking same way there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:55&lt;/strong&gt; · So let me I&apos;m going to start typing and I want you to to interrupt when you have a better idea of how to prompt it for what I&apos;m saying. Sound good?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:07&lt;/strong&gt; · Perfect. [snorts] You like interrupt [laughter] me. You know, don&apos;t say please. And also like, you know, feel free in chat, you know, to give us any ideas on what to tweak as we go to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:20&lt;/strong&gt; · Do I even need to say please create a plan? Like actually how how do I prompt this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:24&lt;/strong&gt; · Put it in planning mode and say like, you know, um I want to now use like a feature uh folder architecture with routing and I want to have like undo you know, like like list like what you the goals are. And it&apos;ll create a plan from that and it&apos;ll say like, you know, hopefully have you review that plan and then you can go forward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:48&lt;/strong&gt; · All right, so I want um routing. I&apos;m just going to put routing and let we&apos;ll see what it comes up with. Uh state like a state management package and history for what colors have been generated. And then I want to be able to revisit old like past previous colors. Also maybe an ability to add a custom color or something. That&apos;s maybe something I would take input. Oh, sure. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:25&lt;/strong&gt; · Uh the ability to add a custom color instead of only generating random colors. We&apos;ve got a question from from chat here. Folks may know, Emma asks, &amp;quot;Is it true that saying please makes it better?&amp;quot; This has been rumored for, you know, years at this point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:51&lt;/strong&gt; · [laughter] We could try. We could ask uh say please, please, please. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:56&lt;/strong&gt; · [laughter] I beg you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:00&lt;/strong&gt; · One one thing before we proceed, uh could we check what the settings are right now for our artifact review policy? And uh the reason why I ask is because I want to make sure that the agent takes the time and like and like asks for your input. Um the way to get there is you can press on the the uh uh anti-gravity uh settings. And then yes, so right now the review policy says agent decides. Let&apos;s change that to always be uh request review.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:23&lt;/strong&gt; · And then the reason why is because now when it creates the implementation plan, maybe it thinks it&apos;s straightforward, but you know what, just for the sake of like showing off what it&apos;s able to do, I think it&apos;d be really great for us to um see the the plan, review it, be able to add comments, and then being like, &amp;quot;Hey, LGTM. Let&apos;s go ahead and build this.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:45&lt;/strong&gt; · Also, you may want to kind of start with this mode as you build up confidence with anti-gravity and like kind of switch to those more deciding modes later as you uh uh can kind of trust where it&apos;s going.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:57&lt;/strong&gt; · Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:58&lt;/strong&gt; · This is an interesting question. I haven&apos;t seen this feature, but it seems actually quite reasonable. Like, I&apos;ve given you this really lazy prompt. Can you improve the prompt for me? Which is gotta be the peak laziest thing in the world. But is that a thing? Or is Randall making that up?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:13&lt;/strong&gt; · This This is not a thing, but what you could do as a shortcut to that is you could write a simple prompt, and then you can ask it enhance this prompt for me, please.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:22&lt;/strong&gt; · And then the agent will just enhance the prompt for you, probably most likely. Uh and then maybe it&apos;ll also propose other features. And then if you want, you could just copy that and then be like, &amp;quot;Yeah, let&apos;s do exactly that.&amp;quot; That&apos;s also where I think the implementation&apos;s really nice, though. It&apos;ll take this and and kind of already start to enhance that cuz it&apos;ll list out its thinking and all that. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:42&lt;/strong&gt; · Yeah, I I wonder, I mean, that&apos;s kind of what we were already doing Yeah, exactly. planning mode. So, this might this might be redundant. Let&apos;s just go with uh let&apos;s go with asking it to plan this out. Mhm. Although uh uh we&apos;ve started it. What [snorts] one of you said that maybe it would come up with its own features as well, and I can imagine it really getting creative on new features in the please enhance this prompt style.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:09&lt;/strong&gt; · So, one of the things that I&apos;ve usually done when I&apos;m sending uh prompts to the model uh when I&apos;m starting off like a a bigger task like this, is I usually always suffix my prompts with like come up with a plan and get started to try to trigger like a like thinking through the process of like what it&apos;s going to do, and then um building out the plan. But um that&apos;s like one of the things that anti-gravity just kind of does for you if you&apos;re in planning mode. So, um I found I didn&apos;t have to use that as much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:36&lt;/strong&gt; · Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:37&lt;/strong&gt; · Oh boy, we&apos;re getting a lot of a lot of proposed screens and classes in here. It&apos;s going to flutter riverpod.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:45&lt;/strong&gt; · That is uh And so, do you have an option to select the implementation?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:50&lt;/strong&gt; · All right, so yeah, it popped it up. Okay. And now do you see the GIF here, um oh, I can dismiss this. Is that what&apos;s going on? Okay, good. Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:59&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:01&lt;/strong&gt; · I didn&apos;t see the dismiss button below it. So, as you start to hover, do you see that little blue icon?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:07&lt;/strong&gt; · Mhm. That is the comment button. So, you can like comment on any one of these items in here and like give it your preferences of on what to use. Nice. Also, just so that&apos;s awesome, and I uh yeah, okay, cool. I see that and I should because it&apos;s you don&apos;t have It doesn&apos;t stop the flow. It&apos;ll take your input and just constantly modify as it&apos;s building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:26&lt;/strong&gt; · So, you can you can give it like this live input um and guide it as it&apos;s generating.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:31&lt;/strong&gt; · Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:33&lt;/strong&gt; · Also, this this is the subtlest little thing, but there&apos;s obviously some styling on this markdown. And I just find this to be very pleasant. So, Yeah, that&apos;s a cool quote. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:45&lt;/strong&gt; · Wonderful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:46&lt;/strong&gt; · Okay, let&apos;s skim this. So, refactor a single thing to be more over-engineered. Great. User review required. Yeah, that obviously using riverpod for state management, go_router for routing. Flex color picker, that&apos;s great. Then it&apos;s going to use those packages. Great. Why not? It even wants to add localization. Love it. So, we&apos;ve got feature-based stuff here. The color generator, history.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:14&lt;/strong&gt; · Um I can&apos;t tell if this is like clipping cuz there&apos;s so much under color generator and nothing under history.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:20&lt;/strong&gt; · It&apos;s definitely gone with clean the clean architecture model of like You can you can comment on that and say I want a feature-first folder. Yeah, exactly. I don&apos;t want uh So, yeah, it just literally like uh yeah, yeah, right there. Oh, I got it. You can also select if you if you want to be highly specific, uh you can also just drag and select text that you&apos;d want to comment on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:42&lt;/strong&gt; · Um so, you all you always have those two options, whether you just want to comment on the line, or you can just comment on the text that you have selected. I&apos;m giving it a really like confusing lazy message here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:56&lt;/strong&gt; · I want to see how it Now, is it automatically incorporating this, or do I need to Like, I don&apos;t want to say proceed on the whole thing, but I do want it to proceed on taking this comment. So, what do I do with that? So, you have a couple of options here. You can uh maybe just click on the review button, and then uh here add an optional message and click submit. Or if you want to go back to what you all are used to, you can also do that inside of the the prompt box inside of the at the bottom right um to submit the comment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:28&lt;/strong&gt; · Then as you can see, the comment is already attached to your input box. Yeah, this is like a GitHub code review. Where you can leave a bunch of things and then submit them all at once. All right, so I&apos;m going to lean into that. See if we have any other suggestions here. Controller. Manages current Sure, that seems reasonable. Routing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:51&lt;/strong&gt; · Yeah, the history screen. So, okay, I&apos;m going to put another thing here like I want a navigation stack that goes backwards through chosen colors, not just one screen that lists all past colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:12&lt;/strong&gt; · Obviously.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:12&lt;/strong&gt; · way cheaper and faster to make decisions here than it is later. So, uh definitely make sure to get this implementation how you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:21&lt;/strong&gt; · Oh, right, right. Yeah, once it&apos;s written all the code and I&apos;m like boo, then I&apos;ve wasted a whole bunch of you know, my own tokens and my own bill, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:31&lt;/strong&gt; · Okay, so to play blah blah blah, generate, yeah. Color Okay, that seems reasonable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:37&lt;/strong&gt; · History.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:39&lt;/strong&gt; · So, remember that this is a navi- or like um yeah, history stack. I forget, you know, kind of a navigation stack like history or chain stack. Yeah, undo, redo stack. Yeah. Not just one page. Yeah, yeah, yeah, yeah. Perfect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:56&lt;/strong&gt; · [gasps] Got some color. Yeah, yeah, yeah, yeah, verification plan. Well, one day we&apos;re going to see this run in the browser, and it&apos;s going to be awesome. So, yeah, submit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:08&lt;/strong&gt; · I don&apos;t know why, I just think it&apos;s really cool to see like when it&apos;s submitted and you see how like the count of comments that it it reads and like the context. It&apos;s so uh I just every point in this editor, it&apos;s like it&apos;s giving you confidence that it&apos;s like uh reading the right type of things. So, um yeah, it&apos;s super cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:26&lt;/strong&gt; · [laughter] I love the idea of putting make no mistakes at the end of any prompt. And also like don&apos;t go jump.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:34&lt;/strong&gt; · Uh let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:37&lt;/strong&gt; · [sighs] Skimming chat here while it cooks to see other other questions. Folks, normally I read a much higher percentage of chat messages, so I apologize for that on this. Um I am trying to read as many as possible. Randall apparently had some genius idea earlier. Let&apos;s see what he said before. Oh, that was the enhance the prompt thing. Okay, great. We We talked about that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:04&lt;/strong&gt; · Uh Danny, Danny is the maintainer of the Flutter and Dart VS Code extension, which I presume is the same one used in Cursor and anti-gravity and all of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:17&lt;/strong&gt; · So, Danny is asking, &amp;quot;Where&apos;s best place to send feedback?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:22&lt;/strong&gt; · Mhm. Yeah, that&apos;s a great question. Uh best way to send feedback right now is if you open the settings window, um there is a place where you can uh so, open anti-gravity user settings, and then at the bottom left, there is a provide feedback section. Uh and this is where you can just provide any type of feedback that you have. Um make sure you submit that. I think the dev team would be very happy to see this in the the feedback form.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:49&lt;/strong&gt; · Great, I&apos;m putting it in. It&apos;s not really a bug report. I guess it&apos;s just general feedback. Yeah, and this Yeah, it&apos;s me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:55&lt;/strong&gt; · And the nice thing about this is that it has all the labels needed, so we can just uh have it be a lot more organized in that way. We can just get to your uh feedback as soon as possible. As you can imagine, we&apos;re getting a ton a ton of feedback, so it&apos;s it&apos;s really nice to have it like organized, so that we know exactly like, &amp;quot;Okay, like these are the ones that are features, these are the ones that are bugs, and how we can prioritize that to make your developer experience better.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:21&lt;/strong&gt; · All right, so this must be just kind of representative cuz we lost history entirely, but I trust that it&apos;s still going to add that. No, it&apos;s kind of dropped. Oh, maybe the navigation is just going to yeah, be baked in. So, it&apos;s not really its own feature anymore. All right, it&apos;ll be interesting to see Yeah. maybe the color controller is just going to have that now? Yeah, we&apos;ll see what it does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:41&lt;/strong&gt; · Okay, I&apos;m going to say proceed. I&apos;m ready to roll the dice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:45&lt;/strong&gt; · Do we need to um change the editor settings to make it to agent decides or will it request each time?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:53&lt;/strong&gt; · Now it&apos;s going to request each time, uh but you can always go back to the settings to change it back to um agent decides or right below the proceeded with implementation plan, there&apos;s a shortcut where you can click on request for uh request review on the right side and there&apos;s a drop-down where you can just configure it uh for your entire user settings. Oh, so this is not just for this conversation. This is a global change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:17&lt;/strong&gt; · Yes, exactly. Okay. All right, well let&apos;s put it on agent decides because that&apos;s what I want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:24&lt;/strong&gt; · [laughter] Uh all right, going back to chat here. Ooh, every now and then I This is another form of rolling the dice where I just see a long message, the first few words seem reasonable and I click it without reading the whole thing. Here we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:40&lt;/strong&gt; · Uh missed the start of the stream. Is there uh will there be a Flutter sub-agent support for anti-gravity to see into actually running Flutter apps in a simulator? Actually, this is kind of related because we&apos;re about to run it in the web app, the browser, and see it drive there. But yeah, any any connection with simulators? I imagine that&apos;s maybe a bigger challenge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:02&lt;/strong&gt; · So, um right now there&apos;s the the dev tools like MCP server control for browser specifically and that uses like Chrome dev tools like deeply integrated. Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:14&lt;/strong&gt; · But we as Flutter developers can also take advantage of the Flutter driver where it can start to write and run integration tests and smoke tests. Um um on these like kind of different things, they can like write a specific test to instrument what you&apos;re trying to do um and that would give you a lot better results cuz you&apos;d have the access to LSP and all the logs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:37&lt;/strong&gt; · That makes sense. Yeah, so [sighs] of course that&apos;s running through an MCP browser server and that makes sense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:43&lt;/strong&gt; · When I before I was at Google, I was at Zapier, which is kind of like you know, it&apos;s the connective tissue of the internet. You can have the outputs from one service be the inputs to another service and it it just provides this standardized kind of like pub sub for the whole internet. And of course, MCP is that on your computer for all the different things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:04&lt;/strong&gt; · Um So of course, that runs through an MCP server. And it&apos;s also like really deep control. Like it can have specific logs from V8 directly as opposed to um just like high-level ones. All right, let&apos;s see. Do we have Oh, yeah, we got some red here. So that means code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:26&lt;/strong&gt; · All right, so this code was waiting for me to accept it, which of course was possible because we put it on agent decides and then it decided that I needed to accept this or not. But other than seeing that there was some red here and thus deducing that it had written code, like let&apos;s say I was just sitting back here and I didn&apos;t open this file myself and I&apos;m I&apos;m just kind of waiting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:51&lt;/strong&gt; · What is the intended flow for me to interact with the progress that it is making?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:00&lt;/strong&gt; · Yeah, I think that&apos;s a that&apos;s a good question. I think something that we&apos;ve been trying to uh figure out was the the best way for developer. I think when you&apos;re looking at the conversation, that is like one way you could be spending your time, which is just tracking and seeing what is the work that it&apos;s doing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:14&lt;/strong&gt; · So that you always have the ability to course correct uh as needed. Um there&apos;s also, if you want to review the current changes that it&apos;s currently done, there&apos;s the review changes button, which is right above the stop button uh on the bottom right. Um it&apos;s a little less visible, but yes, that&apos;s the that&apos;s the place where you can review the code changes if you care about uh all the code that it&apos;s written. Um otherwise, I think it&apos;s more of like a hey, you could just sit back, relax, and let the agent cook. And from there, once it&apos;s done, it&apos;ll give you a walk-through of everything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:46&lt;/strong&gt; · Um and then that way um you get a summarized version of all of the work that it&apos;s done. So as you can see right now, because [clears throat] the agent is still running, you don&apos;t have that walk-through yet. Uh and that&apos;s because, you know, it&apos;s it&apos;s still working. But once it is done, it will create a walk-through of everything and the screenshots uh of of the web app that you just built. This could also be where you open up command E, go to a different project while it&apos;s doing this, and you&apos;d get notified when it&apos;s finished.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:14&lt;/strong&gt; · So like that&apos;s where the async manager would really start to come into play. Uh you would you know, like sometimes it can take a while to build the the iOS and Android application, and so you could be like on a different task and uh yeah. Yeah, that that&apos;ll really, you know, that&apos;ll task that will stress test my ability to think about two things at once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:38&lt;/strong&gt; · [laughter] Like yeah, the the bottleneck is me. You&apos;re single-threaded, so yeah. Right, exactly. All right, let&apos;s see what it came up with here. So Oh man, this is great. Now I don&apos;t I don&apos;t know how it&apos;s going to handle kind of like forks in the nav history, so let&apos;s Orange wasn&apos;t part of us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:00&lt;/strong&gt; · custom color picker on the top right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:03&lt;/strong&gt; · Oh yeah, so wait. So if I give if I go back in time and then generate a new random color, we lose the history in front of it. That makes sense. I was wondering how it was going to handle that edge case. And then we&apos;ll pick a custom color as you said. Oh my goodness gracious. Look at this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:20&lt;/strong&gt; · Yeah, that&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:21&lt;/strong&gt; · Uh now I was kind of expecting the like color wheel, but be an option you could refine later, but yeah, it&apos;s a Ooh, that&apos;s a that&apos;s a hefty color there. That&apos;s really limey. I do feel like this is a just like a good example of one, something that already looks better, you know, by default. Um Gemini 3&apos;s I feel like a lot better at UI design and uh also just like more technically deep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:47&lt;/strong&gt; · [snorts] Yeah, this is I mean, you know, obviously we got to ship this. This is like a great startup idea. Uh all right, I want to see you mentioned the walk-through and just such a file literally labeled walk-through has appeared. So this is presumably the beginning of the answer to my next cute question, which was how do I look at the walk-through?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:08&lt;/strong&gt; · So, let&apos;s see what it said. It believes, and I agree, that it has successfully refactored the application in the requested way. The code is organized by feature color rather than by layer. Wonderful. Uses Riverpod, GoRouter, and generates random colors. I saw that. It did it. And has the history.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:29&lt;/strong&gt; · Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:30&lt;/strong&gt; · Yep, generating a new color clears the forward history, standard behavior. Yep. As as witnessed by us. It&apos;s got the picker. Yeah, it didn&apos;t uh wasn&apos;t what I was expecting. Maybe we&apos;ll come back and ask it to give us a color wheel or maybe that&apos;s not actually that important for the purposes of this stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:50&lt;/strong&gt; · So verification, it [clears throat and cough] Oh, oh right, right, right. Our whole original thing, my original question was like to what extent can it kind of put on its Selenium hat and drive the UI and verify these things itself. I clicked buttons and I verified it, but did did it do that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:13&lt;/strong&gt; · Didn&apos;t seem to do it this time, but what if we just ask it explicitly, can you test this out for me?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:19&lt;/strong&gt; · Okay, I love that idea. Oh wait, there&apos;s another window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:22&lt;/strong&gt; · like do you tag the workflow or should you just like give it a a shorter task?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:26&lt;/strong&gt; · Um I think you could just say like, hey, can you use the browser and test it out for me?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:32&lt;/strong&gt; · And we had two open, so I&apos;m going to close them both. Now review changes, they&apos;re highlighted still in the git diff thing even though I accepted them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:40&lt;/strong&gt; · Yeah, those are effort changes. You can do this later. Yeah. All right, so I&apos;m going to close all these things. Actually, I don&apos;t really care about any of this for now. Oh, nice. Great. Um Yeah, verify is checked. Interesting. I [clears throat] want to watch that happen though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:55&lt;/strong&gt; · So uh yeah, please verify. What was the wording that you were saying? Like verify this works?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:01&lt;/strong&gt; · just like, hey, can you use the browser to verify if this works?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:06&lt;/strong&gt; · Use the browser to ver- verify that this works. One day I&apos;m going to learn how to type, but today is not that day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:19&lt;/strong&gt; · [sighs] All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:22&lt;/strong&gt; · Oh man, it&apos;s giving us the the process ID. Well, that&apos;s just thoughtful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:27&lt;/strong&gt; · You can also think about like how many times you&apos;re building an app and you have a specific CUJ in mind where you could like tell it to like test out this flow uh on your application and then see how it works. It&apos;s kind of like a mix between testing and then also debugging um cuz if the if it&apos;s getting caught up on something, maybe there&apos;s something you could simplify or improve there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:52&lt;/strong&gt; · Uh so I think what is going on here is that the agent is trying to decide between using um the Dart uh MCP server versus the browser. And so that&apos;s why a lot of the browser integration is not uh working as expected. Uh we&apos;re at least not Sorry, it is it is working as expected, but it&apos;s mostly the agent is a bit more confused as to which type of tool it should be using.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:15&lt;/strong&gt; · So um I think that uh because the MCP server for the Dart probably also uh carries like, I am also a browser that you can use, um it is a bit more it is always relying on the MCP, which Which why in this case it did not actually leverage the browser. Maybe we could say take a screenshot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:39&lt;/strong&gt; · Oh, actually here. This is spoke to you actually. Yeah, let&apos;s let&apos;s try this out. Let&apos;s try setting up the the browser. All right, moving my cursor off the window. Cursor is over here. Oh, and then you you do need to do a first time installation process. And it&apos;s going to ask you to to sign in and to install Chrome extension. So if you don&apos;t have the Chrome extension, unfortunately the browser integration will not work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:15&lt;/strong&gt; · All right, I&apos;m going to do this off screen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:17&lt;/strong&gt; · [laughter] Yeah. Which is a lesson that I It&apos;s also all that&apos;s like I&apos;m signing into the empty Chrome. Oh, where&apos;s my phone? I&apos;m going to do the QR code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:28&lt;/strong&gt; · Or a passkey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:30&lt;/strong&gt; · Uh while he&apos;s doing that, Andy, another cool little piece of UX I really liked in the chat is the inline terminal that you can even work with interactive commands. I thought that was such a cool touch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:44&lt;/strong&gt; · Yeah, thank you. Yeah, I think that the team has put a lot of thought into this and I think that one of the biggest challenges I remember we faced was the fact that the terminals sometimes it requires you to interact with it. One of the cool things that we recently added is that the agent is also able to just send things that send things into the terminal so they can interact with it for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:08&lt;/strong&gt; · So example, if you&apos;re trying to create a new app and it&apos;s like would you like to use this or that? Instead of having to like open the terminal press enter, the agent&apos;s like I&apos;m just going to press enter for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:16&lt;/strong&gt; · And that really just like drives the this whole like like agent is able to control everything for you. Or if you want to take it over like be our guest and just go go for it. Last night I had it um was working with Docker Compose. And so it would be like spinning up and down containers. And I I found that it would want to check the logs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:37&lt;/strong&gt; · So it open up spin up the containers, run a shell command inside of it, wait a couple seconds, kill the container, and like it was just such a cool like autonomous agent kind of running in the background.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:51&lt;/strong&gt; · Which is awesome. Yeah, what when we first put out terminals and like I saw it was running all these things it&apos;s like wow, like this is incre- this is increasingly like so cool cuz it&apos;s like managing all these things that I don&apos;t have to constantly open the terminal myself and do those things anymore. And that way like you as a developer you&apos;re going to spend more time thinking about like hey, like what do I want to build?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:12&lt;/strong&gt; · Does this look good or not? Should I add comments and course correct it even further? Mhm. Oh, yeah, totally. Um Is there any thing else Yeah, it&apos;s not working. Oh, it&apos;s not working correctly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:25&lt;/strong&gt; · It which is a passkey like Google app. It just is looping through a UX like recursive bit of nonsense. So unfortunately, I don&apos;t think we&apos;re going to be able to watch it drive the browser. Uh but I believe that it could be done. Maybe I maybe my passkey is only half set up or something. I don&apos;t know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:51&lt;/strong&gt; · But anyways, it would install the Chrome extension. And really it&apos;s there to make sure that you have the the Chrome APIs available to that browser. But you should only have to do it once. Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:06&lt;/strong&gt; · Uh let&apos;s see. A couple other questions here. Is there a code dot command for gravity? I I think what you&apos;re the what Flutter Universe here is asking is how you can say, you know, code space a thing and it&apos;ll open that in Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:20&lt;/strong&gt; · VS Code. And yeah, it&apos;s AGY and it self installs when you download AGY. And if you&apos;re asking something else, then please clarify.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:30&lt;/strong&gt; · [snorts] Yeah, is is AGY the terminal command for that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:34&lt;/strong&gt; · I believe it Okay. Okay, continuing to read. There&apos;s some debate on whether or not it should be shortened to anti or grav. And we got at least one vote for grav.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:50&lt;/strong&gt; · [laughter] In colloquial conversation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:55&lt;/strong&gt; · Another vote for grav. Uh I think the these folks may have an uphill battle to climb. But I wish you well in your quest to you know, make fetch happen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:07&lt;/strong&gt; · Um maybe something that people could try if they&apos;re downloading anti-gravity for the first time and want to try the async manager is to pick a package you&apos;re working on and open up the example as its own subproject. Because it will have an application open that is specific for you know, consuming the the package. And you can send tasks that are like to the main package of like I want to add this feature. And then in your your example you say like I want to update the UI to match this feature. And they can be working in parallel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:41&lt;/strong&gt; · And you know, while you could do that from a single repo, it could allow you to start to experiment and see like how you could use these as completely separate processes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:51&lt;/strong&gt; · Yeah, I was thinking about that exact scenario the other day of you know, getting around my Oh, poor thing. It&apos;s still trying to talk to the browser. I&apos;m going to have to say deny. Sorry, I tried. Um [laughter] Feeling bad for Yeah, it&apos;s trying so hard. Um Oh, and we got a walk through of the attempted thing. What it would have done if I hadn&apos;t been the the weak link in the chain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:24&lt;/strong&gt; · Um Yeah, having like a full stack application and you you know, one browser is working on maybe use the planning step first to define the the exact schema and everything, the the communication protocol for how the front and back end are going to talk to each other. And then unleash one agent to work on the front end and one to work on the back end.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:48&lt;/strong&gt; · That&apos;s probably the limit of where I can imagine my brain keeping up with multiple things running concurrently, but who knows. So we could look at all the code that it wrote. Um most episodes on this live stream are in fact about Flutter code. This one less so. Also the app did seem to work pretty well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:10&lt;/strong&gt; · So I don&apos;t think we really need to spend time nitpicking what it wrote. It probably does also just seem to be fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:18&lt;/strong&gt; · That&apos;s also really a Gemini thing and not even an AGY matter. So any thoughts from either of you two about where we might go next or should we take questions from chat? What do what do you think?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:30&lt;/strong&gt; · I have a something I&apos;d love to try out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:33&lt;/strong&gt; · This isn&apos;t something that like while I was testing out anti-gravity, I was kind of blown away cuz I was recording some some demo videos and I was thinking like oh, like wouldn&apos;t it be cool if like I designed like a concept restaurant and I called it like Maison Anti-gravity. And like and then I had all these like futuristic dishes. And I spun it up and then I I I ran to a little hiccup, you know, cuz it was still in development and none of the images were loading. But it had the website and everything set up. So I was like, okay, like can you can you fix this for me?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:04&lt;/strong&gt; · And I thought it would try to point to some like online like images. But then it started generating images. And that&apos;s when I realized oh, like we are at Google. Like we have Nano Banana. Um and at that point I was like, oh, okay, like it started generating dish futuristic dishes for me. And then using the terminal to move all those images into the appropriate folders. And then afterwards I had a website with really really cool dishes. Like it had like one of the dishes was called like Quantum Carpaccio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:35&lt;/strong&gt; · And then I was like that sounds really good. Um and so I think that would be [laughter] I it tastes it sounds very futuristic to me. So I&apos;m thinking like okay, like this sounds like a really really killer use case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:50&lt;/strong&gt; · Especially with its ability to generate image like given that we are building a web app like why don&apos;t we try out something where see if it&apos;s able to generate an image and see if it&apos;s able to like uh I don&apos;t know. Let&apos;s say let&apos;s say our goal here is to extract like the the most popular colors inside of an image that generated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:11&lt;/strong&gt; · I was also going to think very common thing for Flutter is generating app icons and generating the the favicons and image assets in the web folder which you know, it generates a default when you do Flutter create. But that&apos;s my favorite use case for Nano Banana is like now generate a fun icon for this app as well. Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:29&lt;/strong&gt; · Yeah, that&apos;s a that&apos;s a great idea. And because it&apos;s just baked into the product that has access to your code, you don&apos;t have to do it one place drag it over. But instead the agent will just take care of all of it for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:40&lt;/strong&gt; · Yep, totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:45&lt;/strong&gt; · The caps lock really helps. Also yeah, muting myself also really helps. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:51&lt;/strong&gt; · [laughter] All right, now really impress me. Here we go. It&apos;s not an observable Flutter if I don&apos;t talk while muted at least twice. It&apos;s like lumps in your mashed potatoes. You know, that&apos;s how you know they&apos;re [laughter] homemade. All right, I&apos;m curious to see what happens here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:07&lt;/strong&gt; · have had it generate images with Nano Banana, run terminal commands, and then like format and splice up the image to fit into the asset folders of Mac OS, iOS, Android, and all that. And so like it can basically like generate that for you know, 50 plus images from a single one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:26&lt;/strong&gt; · Oh, that&apos;s interesting. Yeah, the I&apos;ve always used There&apos;s there&apos;s a a Flutter plug-in that, you know, kind of does all the copying and resizing and whatnot for you. I hadn&apos;t thought about I hadn&apos;t thought about burning way more electricity to have an LLM do it. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:40&lt;/strong&gt; · [laughter] Well, it runs uh it it uses Nano Banana once to create a high-resolution image and then uh just like ImageMagick like terminal commands to then splice it and yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:51&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:52&lt;/strong&gt; · [snorts and clears throat] So, Andy, you were talking about an interesting thing there and where I thought you were going to go Oh my goodness, what a What an icon. Great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:01&lt;/strong&gt; · [laughter] So, that&apos;s super great. It even did something that approximates a squircle. I wonder if it actually got the math right on a squircle. I mean, it looks looks pretty squirclly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:12&lt;/strong&gt; · So, did it put this in the uh what is it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:18&lt;/strong&gt; · web favicon.png? It did not do that yet. So, please generates the image and then it&apos;s going to it&apos;s I think it&apos;s still generating still doing stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:31&lt;/strong&gt; · Yeah, so it&apos;s probably still doing some work and Oh, okay. So, now it generates the image, but it hasn&apos;t it does not know where exactly it should put it. So, I think that at this point um you could comment ask it like yeah, like just tell it like like, &amp;quot;Hey, love this image. Um actually, one cool thing that I would I&apos;d love to show off before we do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:51&lt;/strong&gt; · Uh I think you might be able to click on the image here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:54&lt;/strong&gt; · Done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:55&lt;/strong&gt; · Yeah, and you can actually review on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:57&lt;/strong&gt; · So, maybe if you want to like say like, &amp;quot;Oh, like can you add a like a face of a dog in the picture?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:02&lt;/strong&gt; · [laughter] Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:03&lt;/strong&gt; · Oh my god.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:05&lt;/strong&gt; · That&apos;s incredible. Okay, yeah. Well, obviously I want a face of a dog in the middle. So, with uh add the face of a [laughter] husky. Okay, I was going to tell it that I loved it, but now that you&apos;ve informed me I could have a husky for no reason at all in the middle of this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:30&lt;/strong&gt; · So, uh while it does this, as I Honestly, it was a cool image. We&apos;re going to ruin it with this husky in the middle. But, um what you were talking about with the futuristic restaurant and the quantum uh gazpacho or whatever it was.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:46&lt;/strong&gt; · The the images that didn&apos;t load, what I imagined was going on is I thought you were going to talk about cores and having it engineer some solution to a cores problem, which is, you know, of course, the bane of everyone&apos;s existence. So, there was a thing that you said you wanted to try, but where your story went Oh man, this is not better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:08&lt;/strong&gt; · [laughter] Oh, now it&apos;s hiding it. Um [laughter] I mean, it&apos;s not wrong. It&apos;s what I asked. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:20&lt;/strong&gt; · [laughter] Um great. Now with 100% more husky, uh please apply this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:30&lt;/strong&gt; · [clears throat] The other one was objectively better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:34&lt;/strong&gt; · Um what was I Oh, yeah, yeah. So, you your story started with There&apos;s something I want to try and then you told us about the restaurant, but the story ended with it seeming like you getting everything working. So, what was the thing that you wanted to try and can can we still try it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:49&lt;/strong&gt; · So, yeah, what I was trying to do is I was something I was trying to create a very, very simple website. I wanted a website uh I wanted the agent to be very creative uh and Gemini to create uh the designs of the website, the copy for the website, and then also create an entire menu uh for the website, meaning it had to come up with a dish name and all of the ingredients.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:11&lt;/strong&gt; · Um and a lot of it was obviously anti-gravity themed. Um so, it got all that stuff working. Oh, look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:20&lt;/strong&gt; · [laughter] Oh, that&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:22&lt;/strong&gt; · Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:26&lt;/strong&gt; · Okay, [snorts] sorry. Keep going. And now you have a husky icon. Um yeah, so that that&apos;s what I was trying to do and then the the images just weren&apos;t working and I never uh because I was mostly vibe coding, I wasn&apos;t looking specifically at like what kind of errors there were, whether it was cores or whether it was pointing at fake URLs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:44&lt;/strong&gt; · Uh and you know, like agents make mistakes. That&apos;s completely okay, but that&apos;s kind of what we&apos;re here for is to make sure that, you know, we can make sure it&apos;s doing a good job. And so, in this case, I said like, &amp;quot;Hey, like this is the problem. Um or I I think I remember I I interrupted the agent because I was like, &amp;quot;Okay, I think you&apos;re you&apos;re going off track. Uh I have a busy day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:02&lt;/strong&gt; · This is the problem I want you to solve right now. Go fix the images.&amp;quot; And it immediately course corrected and it&apos;s like, &amp;quot;All right, like I let me go ahead and generate images for you instead of like pointing to uh incorrect places.&amp;quot; And then immediately it just generated like six images all at the same time and I was like, &amp;quot;Whoa, like not only was it able to generate images, but it was doing all this parallel.&amp;quot; Um so, it was being as fast as possible with it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:27&lt;/strong&gt; · And then immediately after it went ahead and ran some terminal commands, moved all them to the relevant um directory and then integrated it with my application. And at that point, I was like, &amp;quot;Oh, wow, like it just corrected what I pointed out immediately.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:41&lt;/strong&gt; · Um so, that&apos;s that&apos;s definitely something we could we could try as well over here uh if we want to test out further uh image generation. Um so, yeah, that&apos;s basically like how I went into end with uh and ran into this which is really cool. Oh, maybe there&apos;s also we could um add documentation comments and tests cuz that also could it could show the feedback loop there of the testing as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:07&lt;/strong&gt; · Yeah, definitely. That&apos;s a great idea. Let&apos;s let&apos;s get some tests going here. Uh Danny, know that the favicon is all yours. I&apos;ll I&apos;ll yield it to you if you&apos;re interested. Also, it was very cheap to generate and you can probably ask Nano Banana to make it yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:22&lt;/strong&gt; · But, um Andy, I really liked your comment on the just like having the idea for Nano Banana because I feel like that&apos;s been the whole vibe of anti-gravity in general is like I feel like it&apos;s bringing like the best of all of Google&apos;s tools in like one place and we&apos;re trying to, you know, have an experience where we&apos;re just trying to like reduce friction and enable people to go faster and like hopefully even expose people to all the things that we we even have at our disposal, too. Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:50&lt;/strong&gt; · Totally. I think we&apos;re at the best place to kind of like tie all these powerful tools together. This is so many powerful things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:56&lt;/strong&gt; · Uh and it&apos;s very exciting to see like obviously the power of each one individually, but this is like a really, really good example of where the the total or what&apos;s that expression? You know, you guys know what I&apos;m trying to say, right? Like the total is better than the sum of the parts and I think that this is a really great example of that where you like combine Flutter, you combine it with Nano Banana and a lot of your ideas inside of this amazing tool and suddenly you&apos;re able to just like go crazy with your imagination and just let uh the agent cook. Oh, totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:24&lt;/strong&gt; · Like last night I had it running with Go Lang and Postgres and a Flutter front end all running together in the same project. Like you can you can really like mix and match and it works really great. Mhm. Good to see Astra. Yeah, Atlas is my husky. Even folks who didn&apos;t know I had a husky probably could have guessed it based on the very specific dog selection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:50&lt;/strong&gt; · Um Okay, well, there are so many good questions in chat. I&apos;ve been trying to star them all. I&apos;ve got 10 starred, which is a tiny percentage of the great questions and comments that deserved being starred.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:04&lt;/strong&gt; · Um Oh, yeah. So, we wanted to watch it run tests. I&apos;m going to keep that going by Yeah, sure, whatever. Clicking accept cuz I do want to see that feedback loop, but then maybe we can get to some questions as well as we&apos;re coming up on the last few minutes here. Uh real quick, Andy, can you open up multiple um chats with the same project folder? I haven&apos;t actually tested that specifically, but would it like show up as multiple things in the agent manager?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:34&lt;/strong&gt; · Yeah. Yeah, you could it&apos;s it&apos;s very like end-to-end. You can just have like as many conversations in the same workspace. You can also have like many different workspaces, each with many different conversations. Um so, really it&apos;s like all that flexibility is baked in already. Sweet. Many-to-many. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:52&lt;/strong&gt; · Yeah, great question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:57&lt;/strong&gt; · [laughter] Icon is missing the caption. POV, you&apos;re the favorite toy in the washing machine. That is the vibe of that icon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:08&lt;/strong&gt; · Uh okay. So, did it run the test?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:15&lt;/strong&gt; · I can&apos;t tell if it ran the tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:18&lt;/strong&gt; · It wrote the tests. Oh, and it says they&apos;re all passing. I want to see that terminal output. But, also, you guys were talking in the very beginning about copying terminal you know, piping terminal output from somewhere to another place and I still don&apos;t know what that feature is. You control tilde to open up your terminal?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:36&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:39&lt;/strong&gt; · That didn&apos;t do what I was expecting. Uh Yeah, the control escape basically or I at least on my keyboard. Yeah, it&apos;s not open now. Yeah, there we go. Yeah. Um have I have, by the way, Sublime Text keyboard shortcut, so that&apos;s probably Okay. So, like like maybe just cat the readme, for example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:59&lt;/strong&gt; · Like literally I say cat readme?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:01&lt;/strong&gt; · Yeah, yeah, yeah. Cat space readme or whatever the Okay. And so, you can do two different things. You can like select a region and it should pop up like a little chat thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:10&lt;/strong&gt; · Oh, interesting. Right there?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:13&lt;/strong&gt; · And that&apos;ll send to chat. Or inside of the chat itself, you can use the at annotation and then type terminal and it&apos;ll uh select that as well. Oh, so wait, if it gave me a command, so like let&apos;s see here. This seems to be maybe is this what That&apos;s an inline terminal, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:31&lt;/strong&gt; · Okay, so what what where would I do the at command that you&apos;re talking the very bottom on the chat. Oh, down here? Yeah, you can just do yeah. And I&apos;m familiar with doing this with files and whatnot, but where oh term And terminal is one and if you yeah, you just click terminal and then if you have multiple terminal tasks, it&apos;ll you can pick it by name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:50&lt;/strong&gt; · Uh so you can pick to say oh, okay. So if I were to say here run the tests, is what&apos;s going to happen? It&apos;s going to say dart or like flutter test here? No, it would be like you would say like at terminal, here&apos;s the output logs of the error, you know, something like that. You could have it and it would pull in the terminal as context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:08&lt;/strong&gt; · I see. I see. I see. Okay, great, great, great, great, great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:13&lt;/strong&gt; · And so that&apos;s cool. You can copy it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:14&lt;/strong&gt; · Yeah, nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:16&lt;/strong&gt; · Um All right, neat. I I&apos;m I&apos;m trying to think of a non-contrived way to do that. We know the tests are currently passing. Maybe I&apos;ll just break a test and then we&apos;ll run it and they&apos;ll fail and I&apos;ll ask it to fix it. That might be kind of fun. So you could say like you can even prompt it to say like run flutter test in the terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:35&lt;/strong&gt; · Um and it would pop up or you could even you could just run it yourself even as well. This is funny. It&apos;s showing all the tests for a different workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:44&lt;/strong&gt; · Nice. Oh, I&apos;m in that workspace. How did I get to that window?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:48&lt;/strong&gt; · Okay, well, never mind. Uh it is absolved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:52&lt;/strong&gt; · From having [laughter] It&apos;s always PEBCAK. I don&apos;t know if anyone else here familiar with the phrase PEBCAK, problem exists between keyboard and chair?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:02&lt;/strong&gt; · [laughter] It&apos;s the human error, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:07&lt;/strong&gt; · Okay, so let&apos;s say dart test. Or I mean flutter test I needed to run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:12&lt;/strong&gt; · This is not going to work to hit Is it going to just proxy to flutter test?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:18&lt;/strong&gt; · I&apos;ve never run dart test in a flutter project before. I would presume it isn&apos;t going to work. But actually if it doesn&apos;t, that&apos;s a good way to to to find out why. Great Great point. Yeah, terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:30&lt;/strong&gt; · Um Why is this test failing or whatever?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:33&lt;/strong&gt; · perfect, yeah. It&apos;s such a good See if it can [clears throat] be like, well, you ran the wrong command, idiot. It was such a cool feeling like realizing like, oh, I don&apos;t have to copy these errors anymore.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:46&lt;/strong&gt; · [laughter] Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:48&lt;/strong&gt; · Okay, so Oh, wait, it it said a bunch of stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:52&lt;/strong&gt; · What did it say?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:53&lt;/strong&gt; · Dart test. Yeah, user&apos;s use of dart test could be the root cause. Could be, indeed. All right, that&apos;s awesome. That&apos;s super cool. We saw it work. I typed at terminal, why did it fail? And it ran flutter test instead. So cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:08&lt;/strong&gt; · That&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:10&lt;/strong&gt; · All right, there&apos;s a lot of cool a lot of good chats and I think the time has arrived to actually read them. So [laughter] Picnic, problem in chair, not in computer. I presume that oh in computer was not intended to be capitalized. It&apos;s a good one. Okay, so again, I&apos;m sorry. I&apos;ve definitely failed to highlights or to star so many chats that deserved being starred, but I&apos;m going to go through the ones that I did catch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:44&lt;/strong&gt; · Noe asked quite some time ago, no idea if he&apos;s still here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:49&lt;/strong&gt; · Do we know if the Firebase extension works in antigravity?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:53&lt;/strong&gt; · Yeah, you should be able to install the Firebase MCP server as well as some other Firebase extensions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:59&lt;/strong&gt; · Wonderful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:01&lt;/strong&gt; · Uh Flutter Universe tends to be quite plugged in based on chats over the years on observable Flutter, but maybe the last person on this stream to learn about Nano Banana.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:12&lt;/strong&gt; · So Flutter Universe, Nano Banana is it&apos;s basically just a code name for Imagen, right? Yeah, it&apos;s a fun um I would say, you know, Google&apos;s had image generation for a while, but the thing that uh Nano Banana specifically did was bring image generation down in cost and time, so you can generate images really quickly and the quality is really great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:37&lt;/strong&gt; · Uh it can do text inside of images um and you can generate multiple. It&apos;s embedded in lots of different experiences, so definitely check it out. And you can give it it&apos;s like multimodal inputs, too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:50&lt;/strong&gt; · Yeah, and and yeah, you can do edits as well. Like you know, that was a big use case is you could upload an image and say like remove this and stuff like that. Or you could upload an image of yourself and then upload an image of a place Yep. and then say, you know, put me in this scene and make it a cartoon, you know, all all the fun things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:07&lt;/strong&gt; · I And speaking of Nano Banana, today we all Groq also just launched Nano Banana Pro. Yes. And I&apos;ve seen some examples of it and it is mind-blowing. It really is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:18&lt;/strong&gt; · And [laughter] it&apos;s also integrated with antigravity as well, so definitely worth checking out. Yep. So it&apos;s the pro, it&apos;s not like limits, it&apos;s just it&apos;s the next tier of capability. The coolest demo I saw was like a comic strip, so I have been wondering for people who maybe are storytellers, but not artists, you know, the the trouble with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:41&lt;/strong&gt; · or I imagine the challenge of someone who maybe wants to produce a comic strip cuz they have the story in their head and that&apos;s the medium they want, but they&apos;re not an artist is like all the details of every scene need to be just right and kind of getting that and specifying it can be very challenging.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:55&lt;/strong&gt; · Um Well, specifically with with Nano Banana Pro, you can say like it can provide that reasoning. He&apos;s like, explain this to me in like five different things and it like takes something, breaks it down into subtasks and then like has generating text in each of those sub blocks. So it&apos;s like something that like normal Nano Banana would have struggled with, but like it did a great job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:14&lt;/strong&gt; · Well, interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:16&lt;/strong&gt; · Um get your philosophy hats ready. My daughter in primary school has to create a create a classic webpage with HTML and CSS. Do you think that is still relevant? Well, Marcin Marcin, if that isn&apos;t the billion-dollar, trillion-dollar question of our time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:34&lt;/strong&gt; · I certainly have my thoughts on it, but yeah, Rodi and Andy, what how do you two think about that these days? I I personally think that there is no better time to learn how to publish and share things with the world. HTML and CSS was even how I started back in in high school like trying to just like build something on GeoCities, you know?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:54&lt;/strong&gt; · But now what we do is we have like these amazing tools at our disposal to like build and craft anything that we can imagine. Uh there&apos;s there&apos;s a huge spectrum between like programming and engineering all the way to just like producing artifacts whether it&apos;s just a one-off thing that you&apos;re building for yourself or your kids or, you know, just like that brings you delight or like building businesses on top of it. And so um yeah, I think it&apos;s it&apos;s a great way to learn.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:21&lt;/strong&gt; · I also think that HTML and CSS and JavaScript specifically help you by not crashing when you put in an error or like a invalid token and it&apos;s it&apos;s a great way to like kind of express your ideas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:39&lt;/strong&gt; · Mhm. Yeah, I think I couldn&apos;t have said it better. I think Rodi nailed the point. It&apos;s just very, very useful to learn and it goes a very long way. I I also think that having the ability to or knowing how to use HTML, CSS, JavaScript and so much more technologies also gives you the vocabulary and understanding to just take take your ideas to the next level by understanding a bit of the technology.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:04&lt;/strong&gt; · And so I definitely still recommend learning those those core technologies. Oh, you&apos;re also muted again. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:16&lt;/strong&gt; · Nice. Another lump.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:19&lt;/strong&gt; · The only thing I would add to Also, God bless you two for telling me right away.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:25&lt;/strong&gt; · Um to all of us, you know, Marcin&apos;s daughter, everybody else, if you find the task of thinking about and understanding a certain piece of code fun, you know, HTML and CSS in that example message, don&apos;t let the ability of an LLM to have written it for you stop you from learning it and understanding it and just having that human experience of the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:53&lt;/strong&gt; · euphoric like, I know how this works and it you know, I executed it correctly and I implemented it and I could fix it if I needed to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:02&lt;/strong&gt; · If she&apos;s enjoying HTML and CSS, I would absolutely encourage her to keep going in that direction, but nobody loves every part of technology. Like I&apos;ve been a web developer for 15 years. When you start getting into the operating system level and like really low-build system stuff, I&apos;m out. I just don&apos;t care.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:23&lt;/strong&gt; · [clears throat] And so there I very happily let these tools kind of pick up, you know, the baton at that point for me because that is just not what makes me happy. It is not why I got into this and stayed into this or stayed in this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:37&lt;/strong&gt; · And so I would still let for all of us, let your just kind of humanity and your curiosity and what you know, what gives you what sparks joy, you know, still pursue those things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:52&lt;/strong&gt; · Totally. That&apos;s my that&apos;s my thinking. I also think even along that same vein is like use what you do know and combine it with the LLM to help you learn something you don&apos;t know. So like when I have been learning C recently, just like native&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:10&lt;/strong&gt; · built-in C without C++, I would be I would write a function in dart and I&apos;m like, can you explain this to me if it was written in C or assembly or some other language that I don&apos;t know and like being able to see a language that I can read like in Dart or JavaScript or go can start to help you like see multiple examples and help you bridge that gap and LLM&apos;s are really great at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:33&lt;/strong&gt; · Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:34&lt;/strong&gt; · At the end of the day LLM&apos;s are not only for they&apos;re not only for building they&apos;re also for learning. They&apos;re honestly probably one of the best tools for learning so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:42&lt;/strong&gt; · 100%.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:44&lt;/strong&gt; · Absolutely. A while ago a couple years ago I had a a project for on the Flutter team where I had to learn and write some C. Didn&apos;t really enjoy that project but you know Gemini was called Bard at the time and it was still it was my C mentor and it was so much nicer I could be like you know I know I need a thing I need to like copy a pointer of a string and add the thing to it and I need to not leak memory and I don&apos;t want to become an expert at this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:13&lt;/strong&gt; · Please write me that C snippet and you know then I would take the time and read it and be like okay I see how that works. It&apos;s so nice so nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:22&lt;/strong&gt; · like Dart FFI you know it&apos;s been really useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:24&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:25&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:26&lt;/strong&gt; · Um let&apos;s see feature request here. Majid we saw how to submit feedback so I invite you to submit this feedback but yeah a direct Stitch integration excellent idea. So you could do that already with the Stitch MCP server that you could add. Brilliant. I didn&apos;t know Stitch had an MCP server but of course it does. At this point I should just assume every single entity in the universe has an MCP server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:49&lt;/strong&gt; · Another call out here for Figma design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:52&lt;/strong&gt; · Now there is a Flutter to Figma MCP server and I actually had the maintainer of it on observable Flutter I don&apos;t know however long ago. You can find it in the history and that MCP server is awesome so you could certainly get that going. I don&apos;t know if there&apos;s anything specific And Figma should have an MCP server too. For sure. I don&apos;t remember if there&apos;s basically depends on the Figma MCP server. I mean it it would make sense that it did.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:21&lt;/strong&gt; · So I would maybe consider this one just a checked box and it&apos;s a really good flow. Like I was blown away by in this episode all of our ability because we can all install these tools to just be like this component in Figma give me the Dart code and it was really accurate. It was super good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:39&lt;/strong&gt; · But you could also use browser use there too. You could say like here&apos;s this the the Figma design project that I have open. Open up the browser go to this component take a screenshot and implement that. Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:52&lt;/strong&gt; · All right I&apos;m scrolling through the last chat messages cuz I got to the end of the ones that I starred so reading what people have said while I had the starred list up. Um oh actually someone else was just asking about MCP. Actually I think this is the same person asking again which is fine. Um and I did show how to use it in that episode that I was just talking about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:12&lt;/strong&gt; · [snorts] Uh do do do [singing] All right I think we have kind of done it. Any any final thoughts Roddy anything Show show them a link to where to where to find it. Oh yeah links are always hard but you two start sharing some final thoughts and I&apos;ll pull that up. Cool. Yeah Andy. Um this has been really really fun. I think that anti-gravity is a both a joy to build and to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:38&lt;/strong&gt; · It packages all of the things that Google has the really cool like browser integration with Chrome as well as Nana banana Gemini 3 and so much more and I hope everybody who uses uses it has as much joy using it as I did both building and using it on my own free time. Uh Roddy?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:56&lt;/strong&gt; · Yeah I I really like having all the Google parts coming together in one awesome engine and on top of that it&apos;s really kind of refreshing to be able to start to experiment with the async manager of like now I can think it like a 10,000 foot view across my entire machine and like here all my projects and like let me some tasks I may kick off but not&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33:24&lt;/strong&gt; · review for 3 days from now you know and now I can be like working and keeping on task or you know iterating a lot better than you would instead of having to just like open up VS code like in the chat history goes away or even if it&apos;s persistent this kind of like lets you really stay focused and intentional about what you want to work on and what you want to improve over time. Not only that like I feel like there is just so much to improve from here like hopefully this is the worst version of anti-gravity ever and it gets some It will be. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33:57&lt;/strong&gt; · We can promise that. Yeah. That reminds me one of my biggest pet peeves ever is every time the fruit company announces new hardware and they&apos;re like this is the best iPhone we&apos;ve ever made. Like no duh. You mean you didn&apos;t make the last year&apos;s one worse? Like of course it&apos;s the best that you&apos;ve ever made.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:16&lt;/strong&gt; · Uh all right I&apos;ve got maybe the least helpful banner that anyone has ever seen. This is where you find the MCP server guide and no one wants to type in a URL so just search in YouTube for observable Flutter MCP or sorry observable Flutter Figma and you&apos;ll get a great guide there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:35&lt;/strong&gt; · [gasps] All right And then also anti-gravity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:39&lt;/strong&gt; · Yeah. Uh it should just be anti-gravity.google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:43&lt;/strong&gt; · Yep. Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:45&lt;/strong&gt; · Oh that&apos;s what you meant. That makes so much more sense [laughter] than we were just talking about the Figma thing so I thought that&apos;s what you meant. Yeah. No that&apos;s It&apos;s a shared context issue sorry about that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:56&lt;/strong&gt; · That&apos;s a lot smarter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:57&lt;/strong&gt; · [laughter] So this is where you download anti-gravity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:35:03&lt;/strong&gt; · [gasps] Oh gosh that&apos;s funny. Okay folks thanks so much for tuning in. I had a lovely time. Andy Roddy you two are the best. Uh for viewers next week is a US holiday so I&apos;ll be off and then the week after that I am just busy so I&apos;ll be off that week as well and then we&apos;ll hopefully&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:35:28&lt;/strong&gt; · and I when I say hopefully I mean I already told a guest that we will stream on I believe is December 11th so hopefully I&apos;ll be back on December 11th and until then everyone happy Fluttering. Awesome. Thanks. Bye. Thank you.&lt;/p&gt;
</content:encoded></item><item><title>How to build with Flutter and Google Antigravity</title><link>https://rodydavis.com/videos/antigravity/how-to-build-with-flutter-and-google-antigravity/</link><guid isPermaLink="true">https://rodydavis.com/videos/antigravity/how-to-build-with-flutter-and-google-antigravity/</guid><description>The guide demonstrates using Google Antigravity with Flutter to build applications by generating detailed implementation plans and executing complex coding tasks autonomously.</description><pubDate>Wed, 19 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;How to build with Flutter and Google Antigravity&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;MpWMx-59PgI&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Google Antigravity: https://antigravity.google/
Flutter: https://flutter.dev&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Hi, I&apos;m Rodie. I&apos;m on devro at Google. I want to show you how easy it is to build with anti-gravity and flutter. Here, I can paste a prompt for an app that is a meal tracker using go router and signals. Both of these things should rely on the model&apos;s ability to go and retrieve information, especially for this step of using SQLite for storage using something other than the default package that people go to. So, let&apos;s go ahead and begin and see how it works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:30&lt;/strong&gt; · One of the cool things about anti-gravity is it creates a plan with a list of steps that it&apos;s going to tackle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · And one of the really cool UX pieces that we have is the ability to comment on each individual piece if we want to change how that implementation is carried out and just to be able to give a little bit of that feedback back to the model. So, here&apos;s the implementation plan. Again, like I said, you can comment using this icon down here. And already it looks like it&apos;s picked up the correct signals package, correct Go router using SQLite 3 and everything else that we were planning on. It can run terminal commands for you without needing approval.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · Of course, you can customize all this in settings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13&lt;/strong&gt; · It&apos;s also really cool how it&apos;s able to tackle all of these tasks. It&apos;ll do them one by one and check them off as it goes, making it really easy for you to pick back up where you left off and to keep the model on track. In this demo, we&apos;re using Gemini 3 Pro preview. You can see at each step what it&apos;s done, the list of MCP servers if it&apos;s relevant, and also things like the implementation plan and tasks that you can always get back to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:41&lt;/strong&gt; · Because it&apos;s natively integrated, it&apos;s able to pick up on errors and other things from the LSP from the editor. And you can also use things like the Flutter extension if you want to add the MCP server directly. In this case, we already have Dart and Flutter installed as extensions. And because it has the same VS code like marketplace, you can just find your favorite one and it should work here with no issue. We can see it&apos;s already starting to run the application which it should pop up shortly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:09&lt;/strong&gt; · So we did get an error which is common sometimes when you need to set up Coco Pods but as you can see it&apos;s already jumping in and proactively fixing those issues for us. We can also open up and see the files it&apos;s created so far.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:34&lt;/strong&gt; · Looks like we got our meal state home screen and our database itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · Cool. Looks like it built successfully and we have our application. So we can go ahead and say breakfast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:57&lt;/strong&gt; · We&apos;ll do 400.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:00&lt;/strong&gt; · Save a meal. And we got our item there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:04&lt;/strong&gt; · We can go ahead and see the calories. We can delete it. That&apos;s all using SQL light.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:11&lt;/strong&gt; · Awesome. So, this is just a sneak peek at anti-gravity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:15&lt;/strong&gt; · But hopefully you can take this a lot further. It makes it really easy now for you to make subsequent changes by asking the model. And when you&apos;re happy with everything, you can just run accept changes, which will save all of the things to disk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:31&lt;/strong&gt; · We can go in here. You can do accept or we can go down here and do accept all which would, you know, accept all the changes. Um, I personally love this feature because you can undo individual files or kind of peacemail what you really want. And I think it makes uh working like that a lot easier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:51&lt;/strong&gt; · It looks like it&apos;s finished through the rest of the tasks and the implementation and we have a working app. All right, that&apos;s it for this one. Thanks. Bye.&lt;/p&gt;
</content:encoded></item><item><title>Intro to Agent Skills</title><link>https://rodydavis.com/videos/antigravity/intro-to-agent-skills/</link><guid isPermaLink="true">https://rodydavis.com/videos/antigravity/intro-to-agent-skills/</guid><description>Agent Skills enable AI agents to provide access to specialized, deep knowledge on demand, solving context bloat and moving development beyond simple code completion for complex, modern tasks.</description><pubDate>Sat, 28 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Intro to Agent Skills&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;4mnP1lRdUm8&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Introducing Agent Skills, a breakthrough method for giving your AI agent access to vast, specialized knowledge that only activates precisely when the task requires it. You’ll learn how to move beyond simple code completion by creating modular, on-demand skills that provide the deep context necessary for complex, modern development.&lt;/p&gt;
&lt;p&gt;Subscribe to Google Antigravity →    / @googleantigravity&lt;/p&gt;
&lt;p&gt;Google Antigravity is an agentic development platform, evolving the IDE into the agent-first era. Antigravity enables developers to operate at a higher, task-oriented level by managing agents across workspaces, while retaining a familiar AI IDE experience at its core. Agents operate across the editor, terminal, and browser, enabling them to autonomously plan and execute complex, end-to-end tasks elevating all aspects of software development.&lt;/p&gt;
&lt;p&gt;To learn more:
Website: https://antigravity.google/
X: https://x.com/antigravity
LinkedIn:   / google-antigravity&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · AI progress moves at an incredible rate and standards, best practices, and techniques are constantly evolving.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:06&lt;/strong&gt; · Sometimes we make big leaps with the models, but other times the tooling can close critical gaps. We&apos;ve moved away from simple code completion to full agentic driven chats. But we&apos;ve hit a wall, context bloat. You can&apos;t fit your entire codebase into the context window without confusing the model or wasting tokens. Agent skills are a great way to provide narrow and deep context for how to do something, but allow the agent to discover them only when they&apos;re needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · Because they can be lazily loaded in, you don&apos;t have to pay the cost of the context bloat for the entire skill definition if it&apos;s not relevant to the task at hand. At its core, a skill is simply a skill.md file in a named folder inside the agent skills local or global directory. The skill.md file is just a markdown file with front matter that includes the name and description that the agent will use to see if the skill makes sense to read in for the current task.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00&lt;/strong&gt; · You can have global skills that apply to the frameworks and languages you work on and project specific skills for libraries, SDKs, and frameworks specific to the app or backend that you&apos;re working on currently. A skill can include other resources, scripts, code examples, and other assets that are needed. It makes it easy to colllocate and distribute the skills with your team and manage them on disk. You can find skills in many popular repositories from GitHub repos or even have the agent generate them for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32&lt;/strong&gt; · Let&apos;s walk through how to build custom skills together. First, we can open anti-gravity and create a new project in the playground. Create a nested folder called aagent/skills/html3danvas and create a skill.md inside of it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47&lt;/strong&gt; · Inside the skillmd, we create triple dashes to indicate the front matter region and provide a YAML name and description. We then can ask Gemini in the agent chat to generate us a skill for working with 3GS and V and how to set up the resize listener as well as the animation loop. We should also ask for a skill for working with the new TSL tiny shader language, which is a recent addition to 3JS and may not be well represented in the training data. After it creates the skills, we then can start a new chat and ask the agent to build us a 3D solar system on the web.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:17&lt;/strong&gt; · The agent should come up with a plan and while generating it should read the skills and load them into context. Agents are getting better with training data and sometimes it&apos;ll be able to solve the problem without skills. But by providing the context, you can influence the specific type and style of output that you&apos;re going for and better convey the intent for when you&apos;re asking for various tasks. These can also be very company and industry specific and you will add more as your project grows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:44&lt;/strong&gt; · Now that we have a project, we then can ask the agent to create a skill for building games with 3JS and another skill for adding HTML guey that sits above the canvas. After that is done, we can start a new chat. We can ask the agent to update our code and create a learning based game for the solar system. Then we can have a generated quiz and facts for each planet that we click on. As it generates, you should see it read in the new skills that we created. And when it is done, you can see if the game works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:17&lt;/strong&gt; · When we decide to share this code with others, this will load the project into anti-gravity and the skills will be discoverable for them, making distributed knowledge possible, meaning that they can have it colllocated with the project. There are many places to find skills and many tools that are being created to make it easier. You can use skills.sh sh to add any number of skills for supported git repos. For example, skills are an open standard and you can check them out at agentskills.io.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:44&lt;/strong&gt; · You can get started today with anti-gravity and skills by downloading the IDE and adding your favorite ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:49&lt;/strong&gt; · We&apos;ll include a link to some good skills to check out in the description. Thanks for watching and let us know how you&apos;re using skills to level up your agentled development. Thanks.&lt;/p&gt;
</content:encoded></item><item><title>Announcing TensorStore, Chrome Root Program, and more dev news!</title><link>https://rodydavis.com/videos/dev-show/announcing-tensorstore-chrome-root-program-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/announcing-tensorstore-chrome-root-program-and-more-dev-news/</guid><description>This developer news show covers key announcements including startup CPU boosts for Cloud Run, the launch of high-performance TensorStore, the new Chrome Root Program, and Google Play&apos;s $2M Indie Games Fund in Latin America.</description><pubDate>Thu, 29 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Announcing TensorStore, Chrome Root Program, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;4n8oeHk9dLw&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 313 | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:10 - New startup CPU boost improves cold starts in Cloud Run, Cloud Functions → https://goo.gle/3xUAwej&lt;br&gt;
0:34 - TensorStore for High-Performance, Scalable Array Storage → https://goo.gle/3y0lLXo&lt;br&gt;
0:58 - Announcing the Launch of the Chrome Root Program →  https://goo.gle/3xZ3Hge&lt;br&gt;
1:16 - Google Play’s $2M Indie Games Fund in Latin America → https://goo.gle/3dNYzVg&lt;br&gt;
1:43 -Don’t forget to like, comment, and subscribe!&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody from Developer Relations. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow&lt;br&gt;
Subscribe to Google Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] SPEAKER: Hi, I&apos;m Roadie for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:06&lt;/strong&gt; · And this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;h3&gt;New startup CPU boost improves cold starts in Cloud Run, Cloud Functions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · First up, we have an update to Cloud Run allowing for faster cold starts with a new feature called Startup CPU boost.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:16&lt;/strong&gt; · This feature will work with Google Cloud Run and Cloud Functions, second generation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:21&lt;/strong&gt; · With CPU boost enabled, it will allocate more CPU usage during the startup, which will help with serving requests faster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:27&lt;/strong&gt; · For some workloads measured, startup time was cut in half.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:30&lt;/strong&gt; · And you can find out more on console.google.com.&lt;/p&gt;
&lt;h3&gt;TensorStore for High-Performance, Scalable Array Storage&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · Next, we head over to TensorFlow to talk about the newly announced TensorStore, allowing for high performance, scalable array storage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · This is an open source C++ and Python library that is designed for storage manipulation of in-dimensional data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:48&lt;/strong&gt; · This offers asynchronous API, composable indexing, and so much more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:53&lt;/strong&gt; · You can find out more on GitHub.com/google/tensorstore.&lt;/p&gt;
&lt;h3&gt;Announcing the Launch of the Chrome Root Program&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · Next, we can head over to Chromium to talk about the announcement of Chrome Root Program and Chrome Root Store.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03&lt;/strong&gt; · This program determines which website certificates are trusted by default in Chrome and enables a more consistent and reliable website certification process across platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13&lt;/strong&gt; · You can learn more at blog.chromium.org.&lt;/p&gt;
&lt;h3&gt;Google Play’s $2M Indie Games Fund in Latin America&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:16&lt;/strong&gt; · And, lastly, we can talk about the newly launched Indie Games Fund in Latin America.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · This will be awarding two million dollars in nondilutive cache awards and hands-on support for small selected game studios in Latin America.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28&lt;/strong&gt; · This program aims to help them grow their business on Google Play and is open to indie game developers who have already published a game, even if it&apos;s already on the Play Store.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:36&lt;/strong&gt; · You can apply at developersonair.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:38&lt;/strong&gt; · withgoogle.com/e vents/indie-games-fund.&lt;/p&gt;
&lt;h3&gt;Don’t forget to like, comment, and subscribe!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:43&lt;/strong&gt; · To learn more about all these week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:51&lt;/strong&gt; · I&apos;m Roadie for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:52&lt;/strong&gt; · Thanks for watching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53&lt;/strong&gt; · And we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:55&lt;/strong&gt; · Hi, I&apos;m Roadie for the-- yeah.&lt;/p&gt;
</content:encoded></item><item><title>Blockchain-Based experiences, Govulncheck v1.0.0, and more dev news!</title><link>https://rodydavis.com/videos/dev-show/blockchain-based-experiences-govulncheck-v100-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/blockchain-based-experiences-govulncheck-v100-and-more-dev-news/</guid><description>Google&apos;s weekly Developer News Show covers major updates including blockchain-based experiences, the release of Govulncheck v1.0.0, new web platform APIs, and insights into Google&apos;s Rust journey.</description><pubDate>Mon, 17 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Blockchain-Based experiences, Govulncheck v1.0.0, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;HVdW-frdFGk&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 350 | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Intro
0:09 - New to the web platform in June → https://goo.gle/44whMQI
1:09 - Rust fact vs. fiction: 5 Insights from Google&apos;s Rust journey in 2022 →
https://goo.gle/471ZVTp
2:25 - Govulncheck v1.0.0 is released! → https://goo.gle/3OdoHIx
2:46 - Enabling New Blockchain-Based Experiences on Google Play  → https://goo.gle/44I9Tau
3:09 - New policy update to boost trust and transparency on Google Play → https://goo.gle/43rjuRG&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody, a Developer Advocate. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google for Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow
Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi, I&apos;m Rody for &amp;quot;The Developer Show,&amp;quot; and this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;h3&gt;New to the web platform in June&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · Now that Firefox 114 is stable, there are some new exciting features coming to the web platform.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · Starting with the WebTransport API, this is a modern update to the websockets, providing multiple streams, uni-directional streams, and out-of-order delivery.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:23&lt;/strong&gt; · Next, the SVG element can now support the cross-origin attribute and will work the same way as the HTML attribute and applies to all the image tags in the element.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:32&lt;/strong&gt; · Some interesting beta releases include the animation composition property with the new array properties in Firefox 115.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · And Safari 17 adds web apps and lots of CSS features, like container style and font face adjust.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:45&lt;/strong&gt; · On the Chrome side for the 115 release, we now have scroll-driven animations and a series of technologies from the privacy sandbox, such as frame fences that allow for embedded content with limits to the communication, including the topics API that allows for interest-based advertising without tracking the sites a user visits, and plenty more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04&lt;/strong&gt; · You can learn more on the web.dev blog and check out the latest video on what&apos;s new in Chrome.&lt;/p&gt;
&lt;h3&gt;Rust fact vs. fiction: 5 Insights from Google&apos;s Rust journey in 2022&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · Rust only reached 1.0 in 2015, which makes it a relatively new language with a lot to offer and discover in terms of performance and memory safety guarantees.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · At Google, we have been seeing an increase in Rust adoption.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · And pulling from over 1,000 Google developers, we can address some rumors about Rust head on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27&lt;/strong&gt; · Rumor number one-- Rust takes more than six months to learn, which is debunked.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33&lt;/strong&gt; · Based on our studies, more than 2/3 of respondents are confident in contributing to a Rust code base within two months.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:40&lt;/strong&gt; · Rumor number two-- the Rust compiler is not as fast as people would like it to be, which is confirmed by the developers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46&lt;/strong&gt; · Only about 40% of the respondents are finding the speed acceptable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:50&lt;/strong&gt; · Rumor number three is unsafe code and interop are always the biggest challenges, which is debunked.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · The top three challenges include macros, ownership and borrowing, and async programming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · Rumor number four is about Rust&apos;s amazing compiler messages, which is confirmed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:07&lt;/strong&gt; · And rumor number five is that Rust code is high quality, which is also confirmed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:12&lt;/strong&gt; · 77% of developers were satisfied with the quality of the Rust code, and more than half say that the code is incredibly easy to review.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:19&lt;/strong&gt; · Stay tuned over the next year for another update, and check out the &amp;quot;Google Open Source Blog&amp;quot; for more info.&lt;/p&gt;
&lt;h3&gt;Govulncheck v1.0.0 is released!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:25&lt;/strong&gt; · Heading over to Go, we can talk about the exciting announcement of Govulncheck, which is a command-line tool to check vulnerabilities in your codebases and binaries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:33&lt;/strong&gt; · There is a great video from Google I/O this year you can watch titled &amp;quot;Build More Secure Apps With Go and Google.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:40&lt;/strong&gt; · The tool is powered by the Go vulnerability database, and you can learn more at vuln.go.dev.&lt;/p&gt;
&lt;h3&gt;Enabling New Blockchain-Based Experiences on Google Play&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · Finally, we can head over to Android to talk about some policy updates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:50&lt;/strong&gt; · There&apos;s a new policy update which allows new ways to transact with blockchain-based digital content with apps and games on Google Play.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:57&lt;/strong&gt; · From reimagining traditional games with user-owned content to boosting user loyalty through unique NFT rewards, we&apos;re excited to see creative in-app experiences flourish and help developers expand their businesses.&lt;/p&gt;
&lt;h3&gt;New policy update to boost trust and transparency on Google Play&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:09&lt;/strong&gt; · Organizations signing up for Google Play now require a DUNS number.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:13&lt;/strong&gt; · This is a unique, nine-digit identifier that is widely used to verify businesses.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · On your app store&apos;s listing, the Contact Details section is now being renamed to App Support and now includes a new section called About the Developer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:26&lt;/strong&gt; · And finally, there are new expanded developer verification requirements that will help users make confident, informed choices on what to download.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:34&lt;/strong&gt; · You can learn more on the &amp;quot;Android Developers Blog.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:37&lt;/strong&gt; · To learn more about all these week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:42&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:45&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:46&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:48&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Compression Streams support on all browsers, Android’s New Credential Manager, and more dev news!</title><link>https://rodydavis.com/videos/dev-show/compression-streams-support-on-all-browsers-androids-new-credential-manager-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/compression-streams-support-on-all-browsers-androids-new-credential-manager-and-more-dev-news/</guid><description>Google Developer News Show highlights new API support for Compression Streams on all browsers and the public release of Credential Manager on Android for seamless passkey authentication.</description><pubDate>Tue, 07 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Compression Streams support on all browsers, Android’s New Credential Manager, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;RiVJzlTdD68&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 366  | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:08 -  Compression Streams support on all browsers → https://goo.gle/3QOMNLd
0:27 - Android with Credential Manager and passkeys → https://goo.gle/3Mu3hWv
0:57 - More ways for users to identify independently security tested apps on Google Play → https://goo.gle/4632HWD
1:25  - Developer solutions: Unlock and simplify your workflow → https://goo.gle/47ppAVp&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody Davis. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google for Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow
Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:03&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show,&amp;quot; and this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;h3&gt;Compression Streams support on all browsers&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:08&lt;/strong&gt; · First, we can head over to the web to talk about the new Compression Streams API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · And it&apos;s available in all three browsers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · This API makes it possible to compress and decompress using gzip or deflate formats and no longer requires you to include a compression library in your applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:23&lt;/strong&gt; · You can learn more about the API on web.dev.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:26&lt;/strong&gt; · Next, we can head over to Android to talk about the announcement of the public release of Credential Manager.&lt;/p&gt;
&lt;h3&gt;Android with Credential Manager and passkeys&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:32&lt;/strong&gt; · This also includes support for passkeys, which enable a seamless login experience without requiring passwords.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · The new Credential Manager makes it easy to show multiple logins available for each website or app you&apos;re trying to log into.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:45&lt;/strong&gt; · And if you already have a preferred password manager, there&apos;s a new credential provider that allows you to use them with the new Credential Manager.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:52&lt;/strong&gt; · To learn more, check out Android developers.googleblog.com.&lt;/p&gt;
&lt;h3&gt;More ways for users to identify independently security tested apps on Google Play&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:57&lt;/strong&gt; · Next, when it comes to security in Android, privacy is extremely important.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01&lt;/strong&gt; · And now Google Play will show a new banner if an app like a VPN has gone through security testing from an independent party.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07&lt;/strong&gt; · These labels will help in building trust, influencing product decisions, and placing a critical role in consumer risk protection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · These banners are also to highlight developers that prioritize user safety and data transparency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · If you want to learn more, you can check out security.googleblog.com.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24&lt;/strong&gt; · Finally, we have some exciting new launches on developers.google.com featuring three new solutions aimed at helping developers get started when building microservices, three-tier architectures, and serverless web apps.&lt;/p&gt;
&lt;h3&gt;Developer solutions: Unlock and simplify your workflow&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:35&lt;/strong&gt; · Each of these solutions contain a pathway, a video, one click to deploy on Google Cloud, and a quiz where you can earn a badge and save it to your developer profile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44&lt;/strong&gt; · You can learn more at developers.google.com/solutions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:48&lt;/strong&gt; · To learn more about all this week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:58&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Connect Google Services to Bard, Flutter’s Consulting Directory, and more developer news!</title><link>https://rodydavis.com/videos/dev-show/connect-google-services-to-bard-flutters-consulting-directory-and-more-developer-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/connect-google-services-to-bard-flutters-consulting-directory-and-more-developer-news/</guid><description>The Google Developer News Show provides weekly updates on major tech developments, covering enhanced Google Apps connectivity for Bard, WASI support in GO, and the launch of Flutter’s Consulting Directory.</description><pubDate>Mon, 25 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Connect Google Services to Bard, Flutter’s Consulting Directory, and more developer news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;h-PG2r2e8Nk&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 360  | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:09 - Updates to Bard →https://goo.gle/48EOlOO
0:41 - WASI support in GO → https://goo.gle/3PSNItF
0:56 - Introducing Flutter’s Consulting Directory → https://goo.gle/452AIpr
1:19 - How we made Space Invaders: World Defense →https://goo.gle/48wxfmb&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody Davis, a Developer Relations Engineer. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google for Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow
Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi, I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:04&lt;/strong&gt; · This is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:08&lt;/strong&gt; · First off, we have some exciting updates to share about Bard.&lt;/p&gt;
&lt;h3&gt;Updates to Bard&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · It can now connect to Google Apps and services, which means it&apos;s able to pull from data in Gmail, Drive, Docs, Google Maps, and YouTube.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:19&lt;/strong&gt; · Imagine applying for a new job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · You can use Bard to find your previous resume from Drive and continue to collaborate on a new cover letter in Docs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:27&lt;/strong&gt; · We&apos;re committed to protecting your personal information, and the content is never seen by human reviewers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:32&lt;/strong&gt; · You are always in control of your privacy, and you can turn off these extensions at any time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:37&lt;/strong&gt; · Check the description to learn more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · Next, we can head over to Go to talk about some exciting updates with WASI.&lt;/p&gt;
&lt;h3&gt;WASI support in GO&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · Go can now target WASI via a new API in preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:47&lt;/strong&gt; · This allows you to bring WebAssembly out of the browser and run sandboxed applications on the server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:53&lt;/strong&gt; · You can learn more on the Go blog.&lt;/p&gt;
&lt;h3&gt;Introducing Flutter’s Consulting Directory&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · Now heading over to Flutter, we can talk about the newly released Flutter consulting directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01&lt;/strong&gt; · This is a place for design agencies and digital technology consultants to be discovered and to have a place to point people to when they ask about Flutter work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · There is also a Flutter pitch deck which you can copy and help consultants as they ramp up their Flutter expertise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · You can learn more at flutter.dev/consultants.&lt;/p&gt;
&lt;h3&gt;How we made Space Invaders: World Defense&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · And to help celebrate the 45th anniversary of Space Invaders, we collaborated with Taito, which is the Japanese developer of the original arcade game.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27&lt;/strong&gt; · We are happy to launch &amp;quot;Space Invaders World Defense,&amp;quot; which is an immersive AR game that uses location-based technology to explore neighborhoods.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:35&lt;/strong&gt; · In this game, you defend the Earth from virtual Space Invaders that spawn from nearby structures, and you get points by taking them down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · The Geospatial API allows you to attach content remotely to any area mapped by the Google Street View, creating richer, more robust experiences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:51&lt;/strong&gt; · You can learn more on the developer&apos;s Google blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · To learn more about all these week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:58&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:03&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:06&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Dataset Search, Wear OS updates, and more dev news!</title><link>https://rodydavis.com/videos/dev-show/dataset-search-wear-os-updates-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/dataset-search-wear-os-updates-and-more-dev-news/</guid><description>This developer news show provides a comprehensive roundup of the latest Google updates, covering features like enhanced Dataset Search, Wear OS policy changes, Firebase recaps, and new web platform developments.</description><pubDate>Mon, 06 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Dataset Search, Wear OS updates, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;Dg12-kjXMTk&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 331 | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:11 - Datasets at your fingertips in Google Search → https://goo.gle/3ydULmK
0:29 - Firebase 2022 Recap → https://goo.gle/3YrMhTT
0:54 - Policy Updates: New Wear OS App Quality Requirements → https://goo.gle/3ZmDFPC
1:14 - New to the web platform in February →  https://goo.gle/3ZNOt9v
1:37 - Don’t forget to like, comment, and subscribe!&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody, a Developer Advocate. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow&lt;br&gt;
Subscribe to Google Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers #web #data&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:05&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show,&amp;quot; and this is your weekly update on the coolest developer news, from Google.&lt;/p&gt;
&lt;h3&gt;Datasets at your fingertips in Google Search&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · First, let&apos;s talk about how Google is making it even easier to search public datasets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:16&lt;/strong&gt; · While it has been possible to search this data with the Dataset Search, they now appear alongside regular search results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:22&lt;/strong&gt; · This comes at a time when the United States enacted a new policy that requires free access to federally-funded research.&lt;/p&gt;
&lt;h3&gt;Firebase 2022 Recap&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:29&lt;/strong&gt; · Next, we can head over to Firebase to talk about the just released 2022 recap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · This was a very big year for Firebase, including the option to initialize Firebase in pure Dart using environment variables for Firebase functions, a modular JavaScript SDK, Swift SDK out of beta, auth middleware, official Flutter support, extensions marketplace, and so much more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:51&lt;/strong&gt; · To learn more, check it out on the Firebase blog.&lt;/p&gt;
&lt;h3&gt;Policy Updates: New Wear OS App Quality Requirements&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · Heading over to Android, we can talk about some of the new, important policy changes coming for WearOS apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:59&lt;/strong&gt; · These updates include a black background for all apps and tiles, requiring the ongoing activity indicator if used, and displaying the time at the top of the app, while not in dialogs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · To read more about these policies, you can explore them on the Android developer blog.&lt;/p&gt;
&lt;h3&gt;New to the web platform in February&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · Finally, let&apos;s talk about some new exciting updates for the web platform.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18&lt;/strong&gt; · With the latest stable releases of Firefox 110 and Chrome 110, this means that container queries are now supported in all three engines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25&lt;/strong&gt; · Chrome has also added the CSS initial letters property, setting the sync ID for audio context and the launch of the iFrame credential list experiment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34&lt;/strong&gt; · For more info, you can check out the Web.dev blog.&lt;/p&gt;
&lt;h3&gt;Don’t forget to like, comment, and subscribe!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · To learn more about all these week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:41&lt;/strong&gt; · Please, remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44&lt;/strong&gt; · I&apos;m ready for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:48&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:49&lt;/strong&gt; · Then, you scroll down-- go ahead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:51&lt;/strong&gt; · CREW: [INAUDIBLE] RODY DAVIS: No.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · It&apos;s OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · I just-- yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:55&lt;/strong&gt; · I just keep thinking that&apos;s a new sentence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57&lt;/strong&gt; · That would help.&lt;/p&gt;
</content:encoded></item><item><title>Jetpack Watch Face library, Datastream is GA, Bazel Con recap, and more dev news!</title><link>https://rodydavis.com/videos/dev-show/jetpack-watch-face-library-datastream-is-ga-bazel-con-recap-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/jetpack-watch-face-library-datastream-is-ga-bazel-con-recap-and-more-dev-news/</guid><description>This Google Developer News Show covers major updates on the Jetpack Watch Face library, the General Availability of Datastream, a recap of Bazel Con 2021, and other key developer announcements.</description><pubDate>Thu, 09 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Jetpack Watch Face library, Datastream is GA, Bazel Con recap, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;noK1Hnc4LrA&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 273 | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00- Introduction
0:11- Join Cloud Learn to build your Google Cloud skills → https://goo.gle/3y1M1zt
0:45- Develop Watch faces with Stable Jetpack Watch Face Library → https://goo.gle/31HIzh2
1:27- Datastream now Generally available → https://goo.gle/3IEZ0ft
1:58- Bazel Con 2021 Wrap Up → https://goo.gle/3rIklhO
2:34- Recognizing the 2021 TensorFlow Contributor Awardees → https://goo.gle/33d99zL
3:01-  Please remember to like, subscribe, and share!&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Developer Advocate Rody Davis. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below! 😃&lt;/p&gt;
&lt;p&gt;Follow Google Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow&lt;br&gt;
Subscribe to Google Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: I&apos;m Rody Davis for &amp;quot;The Developer Show&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:06&lt;/strong&gt; · This is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · To get us started, Google is hosting a Cloud Learn digital training event for developers, IT professionals, and data practitioners of all career levels on December 8 and 9 at no cost.&lt;/p&gt;
&lt;h3&gt;Join Cloud Learn to build your Google Cloud skills&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · For those in Europe, the Middle East, and Africa, the event will be on December 9 to 10.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:25&lt;/strong&gt; · The interactive event&apos;s purpose is to help equip more than 40 million people with Google Cloud skills.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:31&lt;/strong&gt; · It&apos;ll also include live technical demos, Q&amp;amp;As, career development workshops, covering everything from Google Cloud fundamentals to certification prep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:40&lt;/strong&gt; · You can check out a full listing of everything to expect at cloudonair.withgoogle.com.&lt;/p&gt;
&lt;h3&gt;Develop Watch faces with Stable Jetpack Watch Face Library&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · Over at Android, the Watch Face Studio from Samsung is revealing its Jetpack watch face library.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:50&lt;/strong&gt; · The newly announced developer tool creates watch faces without writing any code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:55&lt;/strong&gt; · The Kotlin-developed library is also useful for developers with a more fine-tuned control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00&lt;/strong&gt; · The release also includes functionality from the Wearables support library, with new features to support customization on smartwatches and a system companion app on mobile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · If you use the Jetpack library to save your watch face configuration options, the values are synced with the mobile companion app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16&lt;/strong&gt; · The mobile app will also automatically present those options in an intuitive interface with previews that update real-time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23&lt;/strong&gt; · A link to all the new features with the library will be in the description box below.&lt;/p&gt;
&lt;h3&gt;Datastream now Generally available&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:27&lt;/strong&gt; · All right, circling back to cloud, Data Stream, Google Cloud&apos;s serverless change data capture and replication service, is now generally available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34&lt;/strong&gt; · This allows you to synchronize data across databases, stored systems, and applications reliably with minimal latency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:41&lt;/strong&gt; · This will also support real-time analytics, database replication, and event-driven architectures.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47&lt;/strong&gt; · It will also allow you to deliver change streams to Oracle and MySQL databases into Google Cloud Services like BigQuery, Cloud SQL, Google Cloud Storage, and Cloud Spanner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57&lt;/strong&gt; · And we can&apos;t forget about BazelCon 2021.&lt;/p&gt;
&lt;h3&gt;Bazel Con 2021 Wrap Up&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · This year&apos;s attendees had a chance to interact with the community through a series of Birds of a Feather sessions with live Q&amp;amp;As.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:07&lt;/strong&gt; · Other sessions included reproducible builds with Bazel, embedded platform testing with remote execution, and Bazelizing a gigantic iOS app, and so much more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:16&lt;/strong&gt; · Also announced, the updated version of the next-gen multilanguage, multiplatform build, Bazel 5.0, which will release soon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:24&lt;/strong&gt; · It includes a new dependency system called Bazel mod.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:28&lt;/strong&gt; · For answers to any of your questions regarding BazelCon 2021, you can reach out to bazelcon-planning@google.com.&lt;/p&gt;
&lt;h3&gt;Recognizing the 2021 TensorFlow Contributor Awardees&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:35&lt;/strong&gt; · And finally, we wanted to recognize the TensorFlow Contributor awardees for 2021.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:39&lt;/strong&gt; · Contributors to the TensorFlow PA write code, teach classes, and answer questions on forums, plus they organize and host events.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · This year&apos;s categories include the SIG Leadership Award, Active SIG Award, and TensorFlow Forum Award, Diversity Inclusion Award, and so much more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:54&lt;/strong&gt; · Congrats to all this year&apos;s participants.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:57&lt;/strong&gt; · Make sure to check out the link in the post for the full list of this year&apos;s awardees.&lt;/p&gt;
&lt;h3&gt;Please remember to like, subscribe, and share!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · To learn more about all these week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:07&lt;/strong&gt; · Please make sure to like and subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:10&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:12&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:17&lt;/strong&gt; · All right.&lt;/p&gt;
</content:encoded></item><item><title>New to the web, try out Android predictive back gestures, and more dev news!</title><link>https://rodydavis.com/videos/dev-show/new-to-the-web-try-out-android-predictive-back-gestures-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/new-to-the-web-try-out-android-predictive-back-gestures-and-more-dev-news/</guid><description>Catch up on the latest Google Developer News Show, featuring updates on Firebase Authentication (MFA, blocking functions), Android predictive back gestures, new web platforms, and AI/ML profiling.</description><pubDate>Thu, 04 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;New to the web, try out Android predictive back gestures, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;6GFeGJ6jAy8&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 305 | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:11 - MFA, Blocking functions, and more come to Firebase Authentication → https://goo.gle/3BCrqoY
0:34 - New to the web platform in July → https://goo.gle/3Sk6ER8
0:56 - Prepare your app to support predictive back gestures → https://goo.gle/3bqZIAU
1:15 - Brushing up on Compose Text coloring → https://goo.gle/3By5iMv
1:36 - Profiling XNNPACK with TFLite → https://goo.gle/3zNVk8x
1:55- Please remember to like, subscribe, and share.&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody Davis from Developer Relations. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow&lt;br&gt;
Subscribe to Google Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi, I&apos;m Rody, for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:07&lt;/strong&gt; · This is your weekly update on the coolest Developer news from Google.&lt;/p&gt;
&lt;h3&gt;MFA, Blocking functions, and more come to Firebase Authentication&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · First up, we have some exciting new features for Firebase authentication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · You can now provide even better protection for your users with multifactor authentication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:19&lt;/strong&gt; · Adding MFA into your existing apps is very straightforward and is coming to Firebase UI and all the platforms very soon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:26&lt;/strong&gt; · In addition, there are new blocking functions that let you check if a user can create an account or log in before finishing the auth flow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · With the latest stable releases of Firefox and Safari, there are some new CSS features that just become possible across all the browsers.&lt;/p&gt;
&lt;h3&gt;New to the web platform in July&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:41&lt;/strong&gt; · First up is the backdrop filter, which makes it possible to apply effects in the area behind the element, such as a grayscale invert or blurring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:49&lt;/strong&gt; · Next, we get the scroll-snap-stop property, which makes it possible to control the snapping behavior of scrolling elements.&lt;/p&gt;
&lt;h3&gt;Prepare your app to support predictive back gestures&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · With the Beta 4 release of Android 13, you can now update your app to support the new predictive back gesture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · This allows you to show a preview of what will happen when you&apos;re swiping back, and this feature requires the app to explicitly opt into the feature and the setting to be enabled in Developer Options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12&lt;/strong&gt; · After Android 13, this will be ignored.&lt;/p&gt;
&lt;h3&gt;Brushing up on Compose Text coloring&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · In Jetpack Compose 1.2.0, it is now possible to add gradients and other color options for text rendering using the regular and shader brush.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23&lt;/strong&gt; · This approach uses the brush API instead of using Canvas or Modify or Blend modes to make it a lot simpler to use and to build custom textiles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31&lt;/strong&gt; · Quick note that this will not work with emojis, as they&apos;re skipped by the shader.&lt;/p&gt;
&lt;h3&gt;Profiling XNNPACK with TFLite&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:36&lt;/strong&gt; · And in case you missed it, XNNPACK is now available on 2.9.1 and later and is a highly optimized library for floating point neural networks interference operators on multiple platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46&lt;/strong&gt; · This library will help in getting the best performance possible for your models while no longer having the profile accumulate all the delegated operators into a single row.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · To learn more about this and all the week&apos;s stories, make sure to check out the description box for all the links.&lt;/p&gt;
&lt;h3&gt;Please remember to like, subscribe, and share.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:59&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:04&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:07&lt;/strong&gt; · Delegated operators into a single row.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:09&lt;/strong&gt; · I&apos;m going to do a redo of that section.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:11&lt;/strong&gt; · There&apos;s a lot in there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:12&lt;/strong&gt; · [LAUGHS]&lt;/p&gt;
</content:encoded></item><item><title>Privacy Sandbox Demos, Pixel devices, and more dev news!</title><link>https://rodydavis.com/videos/dev-show/privacy-sandbox-demos-pixel-devices-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/privacy-sandbox-demos-pixel-devices-and-more-dev-news/</guid><description>This Google Developer News Show covers new announcements including the Pixel Fold and Pixel Tablet, Privacy Sandbox Demos, and improved full-screen functionality with the Keyboard Lock API.</description><pubDate>Wed, 05 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Privacy Sandbox Demos, Pixel devices, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;FjZLZwOJySw&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 348  | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:11 - The new Pixel Fold &amp;amp; Pixel Tablet are here → https://goo.gle/3O2n5Be
0:53 - Better full screen mode with the Keyboard Lock API → https://goo.gle/3PJAPCb
1:23 - Introducing Privacy Sandbox Demos → https://goo.gle/3NDqF3x
1:54 - Google Cloud Awards $313,337 in 2022 VRP Prizes → https://goo.gle/3rdoyvU
2:21 - Don’t forget to like, comment, and subscribe!&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody, a Developer Advocate. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google for Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow
Subscribe to Google for Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google for #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:05&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show,&amp;quot; and this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · The new Pixel Fold and tablet are here.&lt;/p&gt;
&lt;h3&gt;The new Pixel Fold &amp;amp; Pixel Tablet are here&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:12&lt;/strong&gt; · So now, it is time to start optimizing your apps for these large and folding screens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:17&lt;/strong&gt; · There&apos;s a lot of new tools and APIs to get started with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · There are three important design principles you can follow to make most of the large screens on Android.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:25&lt;/strong&gt; · There&apos;s two-pane layouts to display content across two columns, showing you more content across the unfolded, larger screen, and allows you to do more without extra taps or losing context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · App continuity on foldable phones ensures apps easily transition from folded to unfolded orientations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · Responsive layouts resize content and controls when the screen size changes, such as when you use two apps in split screen mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:49&lt;/strong&gt; · You can learn more on the &amp;quot;Android Developers Blog.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:52&lt;/strong&gt; · Next, we can head over to the web to talk about the new keyboard lock API.&lt;/p&gt;
&lt;h3&gt;Better full screen mode with the Keyboard Lock API&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · If you&apos;ve ever been playing that game and you try to close a game dialogue with the Escape key, you know how frustrating it is when it breaks the immersion and leaves the full-screen experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05&lt;/strong&gt; · With the new Keyboard Lock API, available on the navigation.keyboard, you can now lock and unlock to capture all the events.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12&lt;/strong&gt; · If the user presses the Escape key, they&apos;ll just get a dialog that says they need to press and hold Escape to exit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18&lt;/strong&gt; · You can learn more on the &amp;quot;Chrome Developers&amp;quot; blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21&lt;/strong&gt; · Also on the web, there are new privacy sandbox demos to check out, with over 20 APIs to protect people&apos;s privacy online and to help give companies and developers tools to build thriving businesses.&lt;/p&gt;
&lt;h3&gt;Introducing Privacy Sandbox Demos&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:32&lt;/strong&gt; · All of these demos are open source and include lightweight infrastructure scripts bundled as container images.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:38&lt;/strong&gt; · You can run all those demos locally with Docker or deploy to Google Cloud.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:43&lt;/strong&gt; · There are even Google-hosted instances for everyone, including those who are just simply curious and want to start learning and experimenting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:50&lt;/strong&gt; · You can learn more on the &amp;quot;Chrome Developers&amp;quot; blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53&lt;/strong&gt; · 2022 was a successful year for the Google Vulnerability Reward Program, and they identified over 2,900 issues.&lt;/p&gt;
&lt;h3&gt;Google Cloud Awards $313,337 in 2022 VRP Prizes&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · They awarded over $300,000 in prizes and over $12 million to bounty rewards to researchers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:07&lt;/strong&gt; · All of these reports help improve Google Cloud products, which can help users, then customers, as well as the internet, at large.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · You can learn more about the top seven prizes won on the &amp;quot;Google Security Blog.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:19&lt;/strong&gt; · To learn more about all this week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;h3&gt;Don’t forget to like, comment, and subscribe!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:24&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:27&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:28&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:31&lt;/strong&gt; · All of these reports help imploo-- [COUGHS] help imploo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:34&lt;/strong&gt; · One more time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:35&lt;/strong&gt; · Yeah, thanks.&lt;/p&gt;
</content:encoded></item><item><title>Scaling vision transformers, Compose Pager, and more dev news!</title><link>https://rodydavis.com/videos/dev-show/scaling-vision-transformers-compose-pager-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/dev-show/scaling-vision-transformers-compose-pager-and-more-dev-news/</guid><description>The Google Developer News Show features updates on scaling vision transformers, optimizing Compose Pager, exploring WebAssembly advancements, and other cutting-edge developer news from Google.</description><pubDate>Mon, 10 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Scaling vision transformers, Compose Pager, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;wwx-NH4LwrQ&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 336  | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:10 - Wonderous nominated for Webby Award → https://goo.gle/40LIliK
0:28 - How WebAssembly is accelerating new web functionality →https://goo.gle/3ZR0L04
0:54 - Scaling vision transformers to 22 billion parameters →https://goo.gle/3mjc7fG
1:29 - Customizing Compose Pager with fun indicators and transitions →https://goo.gle/43dVOS9
1:48 -  Don’t forget to like, comment, and subscribe!&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody, a Developer Advocate. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow&lt;br&gt;
Subscribe to Google Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [AUDIO LOGO] RODY DAVIS: Hi, I&apos;m Rody for The Developer Show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:06&lt;/strong&gt; · And this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;h3&gt;Wonderous nominated for Webby Award&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · In case you missed it, the Flutter app, Wonderous, just got nominated for a Webby in the best user interface category.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:16&lt;/strong&gt; · You can vote for the app on the Webby Awards website.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:19&lt;/strong&gt; · This app was made in partnership with the gskinner team and since launch has had 30,000 users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:24&lt;/strong&gt; · To learn more about Wonderous, check out the Flutter blog on Medium.&lt;/p&gt;
&lt;h3&gt;How WebAssembly is accelerating new web functionality&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · Next we can head over to Chrome to talk about all the exciting ways WebAssembly is accelerating the web.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · Some powerful real-world examples include FFmpeg on Wasm, background blurring on Google Meet, SQLite on Wasm, which replaces Web SQL, and the universal scene descriptor in Safari.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:45&lt;/strong&gt; · WebAssembly brings faster iteration speed, immediate support across browsers, and improved security.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:51&lt;/strong&gt; · To learn more about WebAssembly, check out the Chromium blog.&lt;/p&gt;
&lt;h3&gt;Scaling vision transformers to 22 billion parameters&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:55&lt;/strong&gt; · If you&apos;re building AI vision models, then you&apos;ll be excited to learn about how to scale vision transformers to 22 billion parameters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01&lt;/strong&gt; · LLMs, or Large Language Models like PaLM or GPT-3 show that scaling parameters improves performance but also unlocks emergent abilities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · But the largest dense models have only reached around 4 billion parameters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · Thanks to VIT-22B, we can incorporate ideas from scaling models like PaLM to a new modified architecture to scale to 5.5 times larger.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24&lt;/strong&gt; · To learn more about this new technique, check out the Google Research blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28&lt;/strong&gt; · Finally, we can talk about the March 2023 release for JetPack Compose.&lt;/p&gt;
&lt;h3&gt;Customizing Compose Pager with fun indicators and transitions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:33&lt;/strong&gt; · There are new composables introduced-- horizontal and vertical pager.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · There are lots of fun transition effects and behaviors that can be applied when swiping through content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · You can also add page indicators, change the viewport offset fraction in the current page and the page state.&lt;/p&gt;
&lt;h3&gt;Don’t forget to like, comment, and subscribe!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:48&lt;/strong&gt; · To learn more about all this week&apos;s stories, make sure to check out the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · I&apos;m Rody for The Developer Show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:58&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Build full-stack AI apps in minutes with Firebase Studio</title><link>https://rodydavis.com/videos/firebase-studio/build-full-stack-ai-apps-in-minutes-with-firebase-studio/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/build-full-stack-ai-apps-in-minutes-with-firebase-studio/</guid><description>Learn how Firebase Studio and Project IDX enable rapid development of full-stack AI applications by integrating natural language prompts for code generation and modification.</description><pubDate>Thu, 22 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Build full-stack AI apps in minutes with Firebase Studio&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;x2zvki_VlRE&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;The way we build software is fundamentally changing with AI-centric tools. This session covers how Project IDX helps you build and ship full-stack apps faster. Your codebase becomes context for Gemini, enabling powerful agentic capabilities in your editor. Generate and modify code or execute commands, all with natural language prompts. You’ll leave this session with strategies to ship better software, quicker.&lt;/p&gt;
&lt;p&gt;Speakers: Kirupa Chinnathambi, Rody Davis&lt;/p&gt;
&lt;p&gt;Check out the cloud session track from Google I/O 2025 → https://goo.gle/io25-gct-yt
Check out out all the Firebase sessions from Google I/O 2025→ https://goo.gle/io25-firebase-yt
Check out all sessions from Google I/O 2025→ https://goo.gle/io25-sessions-yt&lt;/p&gt;
&lt;p&gt;Subscribe to Firebase → https://goo.gle/Firebase&lt;/p&gt;
&lt;p&gt;Event: Google I/O 2025&lt;/p&gt;
&lt;p&gt;Products Mentioned: Cloud&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] KIRUPA CHINNATHAMBI: Hi, everyone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:06&lt;/strong&gt; · Today, we&apos;re going to learn about how Firebase Studio can help you build high-quality full-stack AI apps quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:12&lt;/strong&gt; · My name is Kirupa Chinnathambi, and I&apos;m a PM on Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:16&lt;/strong&gt; · I&apos;m joined by my colleague Rody Davis, who is a developer relations engineer also on the same team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:21&lt;/strong&gt; · We&apos;re going to start by getting a 30,000-foot overview of how app development has changed and why we built Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · We will follow up with a deeper look at Firebase Studio&apos;s marquee features, along with a live demo that really highlights the breadth and depth of capabilities that can help you be productive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · So there are three big changes to app development over the years that played a big role in us building Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · First, app development has gotten really difficult.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:47&lt;/strong&gt; · Building apps today is time consuming, often frustrating, and filled with numerous hoops we need to jump through.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:53&lt;/strong&gt; · And this complexity spans the full software development lifecycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:57&lt;/strong&gt; · All stops on this path are more complicated now than they&apos;ve ever been.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · This complexity is particularly frustrating if you&apos;re a professional developer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06&lt;/strong&gt; · You and I don&apos;t have the luxury of skipping steps or cutting corners.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · Some of the complexities you need to wrangle include answering what your technology stack should be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16&lt;/strong&gt; · What are your dependencies?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18&lt;/strong&gt; · Is what you&apos;re building secure, scalable, and performant?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · Depending on your project, your team, and the industry you&apos;re working in, the types of questions you need to answer can be numerous and varied.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29&lt;/strong&gt; · All of this can be hugely overwhelming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32&lt;/strong&gt; · The software development lifecycle should be smoother and easier for all of us to navigate as we build our apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39&lt;/strong&gt; · A trend that we&apos;ve been observing for some time, that really accelerated in the last few years, is that software development is becoming more decentralized and collaborative across devices, geographies, and time zones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:50&lt;/strong&gt; · It used to be that all we needed to get our apps built was our primary, often powerful, computer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · Now, over time, we started to take our work onto our personal devices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:59&lt;/strong&gt; · These could be our laptops, phones, tablets, and an even smaller devices like watches.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:04&lt;/strong&gt; · Beyond just building our apps across a variety of devices, we also now work from a variety of locations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:10&lt;/strong&gt; · It could be a coffee shop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:12&lt;/strong&gt; · It could be a secondary workplace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:14&lt;/strong&gt; · It could even be our homes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · To add more color to the situation, we now collaborate closely with other people, each of whom could be working on the same project from different locations, different devices, and maybe even different time zones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:27&lt;/strong&gt; · Third, and one that is probably very top of mind for all of us, is the role AI assistants and powerful LLMs are playing in completely changing how we as developers build apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:38&lt;/strong&gt; · This visualization really drives home the two extreme workflows you, me, and our teams are likely adopting when building apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:45&lt;/strong&gt; · On one end, we have a world where we are in the driver&apos;s seat, and the AI plays a purely assistive role.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:51&lt;/strong&gt; · On the other end, we have a world where the AI is doing almost all of the heavy lifting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:56&lt;/strong&gt; · We&apos;re playing more of a consulting role and nudging the AI into the right direction if needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:01&lt;/strong&gt; · The complexity of problems AI can solve, though, has grown by leaps and bounds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:05&lt;/strong&gt; · Today, a quarter of all new code at Google is generated by AI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:09&lt;/strong&gt; · Beyond just productivity, use of AI also tends to increase overall satisfaction and happiness.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:15&lt;/strong&gt; · Here&apos;s where a study that showed individuals assigned to use AI were both productive and more happy than individuals and teams who didn&apos;t use AI at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:23&lt;/strong&gt; · This chart highlights what I said a second ago, where the use of AI pretty much leads to an increase in positive emotions and a decrease in negative emotions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:31&lt;/strong&gt; · This is pretty powerful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:32&lt;/strong&gt; · In recognizing these three major shifts, a handful of us who love developer tools got together a few years ago and asked, can we do better?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:40&lt;/strong&gt; · We landed on a handful of principles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:43&lt;/strong&gt; · First, the ideal environment needs to be ubiquitous.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:46&lt;/strong&gt; · You should be able to build any app from any device, from anywhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:50&lt;/strong&gt; · You and I should have easy access to integrated solutions, not individual puzzle pieces that we have to figure out how to combine in the right way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:58&lt;/strong&gt; · AI assistants shouldn&apos;t just be limited to coding scenarios or tacked on as an afterthought.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:04&lt;/strong&gt; · AI assistance needs to be thoughtfully woven throughout the entirety of the software development lifecycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:09&lt;/strong&gt; · Now, software development has always been a collaborative activity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:13&lt;/strong&gt; · And we need to ensure that our tools help here, where we can be working across a variety of devices, locations, and time zones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:22&lt;/strong&gt; · To help answer the question of &amp;quot;Can we do better?,&amp;quot; we&apos;re thrilled to announce Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:27&lt;/strong&gt; · Firebase Studio is an integrated and extensible agentic workspace to build, run, and manage web apps, cross-platform mobile apps, backend services, and more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:37&lt;/strong&gt; · Let&apos;s walk through some of the high-level features that Firebase Studio brings to the table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:41&lt;/strong&gt; · First, it is fully web-based.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:43&lt;/strong&gt; · All you need is a browser and an internet connection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:45&lt;/strong&gt; · And just because it&apos;s in a browser, though, does not mean you have to sacrifice capabilities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:50&lt;/strong&gt; · You have a full Cloud VM with access to the file system, terminal, and other necessary commands.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:55&lt;/strong&gt; · It&apos;s also built on the familiar VS Code environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:59&lt;/strong&gt; · You have easy starting points, configurable using a technology called NixOS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:04&lt;/strong&gt; · AI assistance and collaborative features are integrated throughout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:08&lt;/strong&gt; · Now, Firebase Studio may look very familiar to many of you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:11&lt;/strong&gt; · It is built on the learnings we received from the hundreds and thousands of you who use Project IDX and gave us your feedback.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:18&lt;/strong&gt; · We actually went public with Project IDX at I/O just last year.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:21&lt;/strong&gt; · So it&apos;s great to highlight the evolution of IDX into Firebase Studio with you all at this year&apos;s I/O conference.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:27&lt;/strong&gt; · Now, let&apos;s take a look at Firebase Studio and walk through its most common features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:32&lt;/strong&gt; · When you launch Firebase Studio for the first time, you&apos;ll have a bunch of options on how you&apos;d like to get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:37&lt;/strong&gt; · You can start with our app prototyper, where you specify what kind of app you&apos;d like to build using multimodal, natural-language prompts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:44&lt;/strong&gt; · In this mode, you can ask us to build an app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:47&lt;/strong&gt; · Once you do that, we first translate your request into something we call a blueprint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:51&lt;/strong&gt; · Think of a blueprint as a one-pager or a mini PRD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:55&lt;/strong&gt; · When you&apos;re OK with the requirements, we start building your app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:59&lt;/strong&gt; · You can continue to interact with the AI assistants to fine tune the app&apos;s output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:03&lt;/strong&gt; · You can draw directly onto the preview surface with our annotation feature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:07&lt;/strong&gt; · You can select elements and describe the changes you would like to see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:10&lt;/strong&gt; · Once you are ready to share your application with the world, you can choose to deploy to Firebase App Hosting or make your app temporarily available with our preview mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:18&lt;/strong&gt; · Now, going back to the Firebase Studio landing page, you can also create a new workspace and build it entirely from scratch or choose from a collection of templates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:27&lt;/strong&gt; · We provide some of the most common templates available to you for easy, direct access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:31&lt;/strong&gt; · You can explore our full collection of templates, which span the most common starting points for frontend, backend, mobile, and more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:38&lt;/strong&gt; · If you want to go one level deeper, below the templates, we give you a lot of flexibility to specify how your environment is built all the way down to the VM-specific details, build tools, and more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:50&lt;/strong&gt; · You can also bring your existing project into Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:53&lt;/strong&gt; · You can choose to open your project from popular Git providers like GitHub, Bitbucket, and GitLab, or you can upload a zip file of your existing project from your local computer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:03&lt;/strong&gt; · Independent of how you get started with building your app in Firebase Studio, we give you full agentic capabilities throughout with our AI assistants.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:10&lt;/strong&gt; · Changes you ask for can be made to a single line of code or to an entire code base.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:14&lt;/strong&gt; · This is the power of Gemini exposed through our app development environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:19&lt;/strong&gt; · Beyond our built-in AI systems, agents from Gemini Code Assist will also be fully available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:24&lt;/strong&gt; · Our goal is to ensure you have the flexibility to choose how much AI assistance you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:29&lt;/strong&gt; · You can use a lot of AI, or you can use some AI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:32&lt;/strong&gt; · You&apos;re fully in control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:34&lt;/strong&gt; · Lastly, we talked about the importance of collaboration when it comes to building apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:38&lt;/strong&gt; · Firebase Studio also share your entire workspace with your colleagues and build apps in real time just as naturally as you might if you&apos;re working physically side by side with someone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:47&lt;/strong&gt; · Before I hand it over to Rody, who will walk through some live demos, a main takeaway is that Firebase Studio is a professional tool designed for all developers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:55&lt;/strong&gt; · Whether you&apos;re AI first, code first, frontend, backend, greenfield, or brownfield, we have you covered.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:00&lt;/strong&gt; · All are welcome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:01&lt;/strong&gt; · Now let&apos;s turn it over to Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:05&lt;/strong&gt; · RODY DAVIS: Hi, my name is Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:07&lt;/strong&gt; · So now it&apos;s time to see Firebase Studio in action.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:10&lt;/strong&gt; · There are two main modes that you can start from inside of Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:14&lt;/strong&gt; · The first is an IDE view, which you should already be familiar with because it&apos;s the same IDE from IDX.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:20&lt;/strong&gt; · And the second is a new app prototyping agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · With this new app prototyping agent, you can build full-stack applications from natural language.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:29&lt;/strong&gt; · It will start by creating a blueprint for us to review, which we can modify at will, and then continue to iterate on in the app with the chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:37&lt;/strong&gt; · When you&apos;re happy with the overview, you can start the app generation, and the blueprint will be saved inside the project alongside the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:44&lt;/strong&gt; · The fully featured application it creates is backed by Next.js, connected to Genkit and Firebase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:52&lt;/strong&gt; · If you&apos;re building an AI feature that needs an API key, we will create a Firebase project for you in addition to generating the API key.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:00&lt;/strong&gt; · As you request new features or modify existing ones, Gemini will update Genkit flows and wire up any of the multimodal requests and handling inside of React for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:11&lt;/strong&gt; · When you feel like the app is in a good place to share, we have an easy one-click Publish button that will take the Next.js application and deploy it with Firebase App Hosting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:21&lt;/strong&gt; · As you make changes to your code, you can also create new rollouts by going through the exact same flow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:28&lt;/strong&gt; · We also support other deployment options, too, which you can take advantage of in the code editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:33&lt;/strong&gt; · Firebase Studio has a powerful integrated development environment which lets you bring existing code bases with any stack configured by the Nix ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:41&lt;/strong&gt; · And there&apos;s also many templates to get started with, with common frameworks and languages that you use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:46&lt;/strong&gt; · For those that aren&apos;t aware, Nix is probably one of the most supported package ecosystems in the Linux world.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:52&lt;/strong&gt; · There&apos;s over 120,000 packages to choose from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:56&lt;/strong&gt; · Every workspace in Firebase Studio is running on a full VM powered by NixOS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:01&lt;/strong&gt; · This means that you can run most commands in the terminal that you would be able to locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:06&lt;/strong&gt; · This could be anything from setting up and configuring databases, to running Docker files, or even running both Python node and go all in the same terminal at the same time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:16&lt;/strong&gt; · Just like how the app prototyper has a powerful chat, we also have the full capabilities of Gemini right inside of our IDE.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:23&lt;/strong&gt; · Gemini can read and write files and run terminal commands with the context of your project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:29&lt;/strong&gt; · You can also choose a custom model with an API key, upload attachments or drawings, and have multiple chat histories persisted for later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:37&lt;/strong&gt; · There are so many more things we did not cover, but make sure to check out our Firebase YouTube channel for more videos and our blog for any updates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:44&lt;/strong&gt; · Head over to Firebase Studio to learn more and try it out for yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:48&lt;/strong&gt; · We are in preview now, and we would love feedback on the product for things that are going great and things that can be improved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:53&lt;/strong&gt; · Thanks for watching, and we can&apos;t wait to see what you&apos;ll build with Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:57&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Building AI Powered Experiences with Firebase Studio and Firebase Platform - Rody Davis Building AI Powered Experiences with Firebase Studio and Firebase Platform - Rody Davis</title><link>https://rodydavis.com/videos/firebase-studio/building-ai-powered-experiences-with-firebase-studio-and-firebase-platform-rody-davis/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/building-ai-powered-experiences-with-firebase-studio-and-firebase-platform-rody-davis/</guid><description>Learn how Firebase Studio simplifies building AI backends by demonstrating how to use the Gemini API within the Firebase ecosystem to rapidly prototype and deploy intelligent applications.</description><pubDate>Mon, 29 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Building AI Powered Experiences with Firebase Studio and Firebase Platform - Rody Davis&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;DqCWNREk7c4&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;The demand for AI-powered features in applications is exploding, but integrating generative AI is often complex and slow. This session introduces Firebase Studio, a new AI powered Cloud Based Workspace on the Firebase platform designed to dramatically simplify building AI backends.&lt;/p&gt;
&lt;p&gt;Join us for a demo-driven guide on leveraging the Gemini API to build sophisticated features with remarkable speed. You&apos;ll leave with a clear understanding of how to use the Firebase ecosystem to rapidly prototype and ship scalable, secure, and intelligent applications&lt;/p&gt;
&lt;p&gt;Sobre Rody Davis: Developer Advocate for Firebase Studio at Google. Working closely with Flutter, Angular, Firebase, Go and Gemini in Google Cloud. In my time at Google I have also supported Material Design, Lit (previously Polymer) and Google Developers (Solutions).&lt;/p&gt;
&lt;p&gt;I have a background in systems administration, audio engineering, music and electrical engineering. I am very passionate about open source and creative applications of software. Currently I am working with teams to take advantage of AI in software development and integration into apps.&lt;/p&gt;
&lt;p&gt;My mission is to make the intersection of Google products work better together.&lt;/p&gt;
&lt;p&gt;🛤️ Track: DEVELOPMENT
📋 Formato: Charla&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Working with AI can vary a lot depending on your skill level, experience, and model provider, or even the AI agents or tools you&apos;re using. At Google, we have many AI offerings ready to meet you where you are and help you take your next or current project to the next level. Here at Firebase, we believe that to make great apps, even through the assistance of AI, will require making it easy to build with agents that can provision resources, author backing code, and build complex UIs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:31&lt;/strong&gt; · So, who here has heard of Firebase?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · Raise your hand. Wow, that&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · What about Firebase Studio?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · Nice. Uh, Firebase AI logic or Genkit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:43&lt;/strong&gt; · Okay, less people, but still cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:45&lt;/strong&gt; · Firebase is Google&apos;s platform to build, prototype, deploy, and run modern AI powered experiences that users love.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:53&lt;/strong&gt; · What makes fair Firebase so special?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:55&lt;/strong&gt; · Well, you know it very well. First, it&apos;s a comprehensive platform. And second, it&apos;s very easy to get started. We will give you a well-lit path to accomplish your objectives. There&apos;s no need to hack together a solution from multiple blog posts and forums or different parts of the internet. But we don&apos;t stop there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · Firebase is backed by GCP&apos;s infrastructure, security, and reliability, and trusted by millions of businesses. And most importantly, it&apos;s extensible and flexible. You get to decide which parts of the platform make the most sense for your application and you bring in the other tools and services as you need.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:35&lt;/strong&gt; · We recently announced Firebase AI logic in the as the next evolution of Vertex and Firebase. We recognize that integrating AI into your applications can be challenging. A year ago, we launched Vertex and Firebase making a lot easier and is already used by thousands of customers in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · For example, who&apos;s accidentally shipped their API key to the client? I know there&apos;s at least one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · Firebase AI logic is one of the easiest ways to integrate, innovate, and deploy AI seamlessly into your apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:09&lt;/strong&gt; · On your client, you can use Firebase AI logic proxy to securely access Google AI and vertex models such as Gemini and Imagine. And on the server, you can use Genkit. Firebase AI logic is integrated with other Firebase services like app check remote config and AI observability and remote config.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · In addition to the Vertex API, you can use the Firebase AI logic client SDKs to connect to the Gemini developer API which has a free tier. And last of not not least, we&apos;re launching Unity support which also includes support for Android XR.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:49&lt;/strong&gt; · We recently updated Firebase AI logic to have an experimental mode for using hybrid inference. This is currently offered on Chrome on desktop. With hybrid inference, your inference will happen on the device if the ondevice is available. Otherwise, it will seamlessly fall back to cloud hosted models.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:09&lt;/strong&gt; · We are also expanding AI monitoring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:12&lt;/strong&gt; · Previously only available to Genkit, you could now monitor your AI logic SDKs coming from the client in a single pane of glass where you can see things such as latency, error rates, token use, or even stack traces. And it&apos;s very useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:26&lt;/strong&gt; · But we didn&apos;t stop there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:28&lt;/strong&gt; · Genkit is also now a part of Firebase AI logic. Genkit, for those that don&apos;t know, is our open- source toolkit for building powerful AI backends, offering libraries, tools, and out-of-the-box integrations with many popular models and cloud services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:44&lt;/strong&gt; · You&apos;ve given us great feedback on Genkit, and I&apos;m happy to say that we&apos;ve heard you loud and clear. At Nex, we officially launched support for Python in Genkit, and since then, we&apos;ve also been hard at work on supporting and improving Go. But we&apos;ve also introduced dynamic model registration, which means you no longer need Genkit to have an update to leverage the latest models, and it can just seamlessly update and have access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:08&lt;/strong&gt; · We&apos;ve recently announced genkit.dev, which you definitely need to check out because it&apos;s our go-to single surface for the most up-to-date documentation and code examples on how to use Genkit effectively in your applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:24&lt;/strong&gt; · Now, almost every developer needs a database and you&apos;ve been asking for Firebase to have a relational database forever when we&apos;ve heard you loud and clear. And just over a year ago at IO, we launched Firebase data connect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:38&lt;/strong&gt; · Firebase data connects to your application to your data in Postgress Cloud SQL through secure serverless APIs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:46&lt;/strong&gt; · We built data connect with one premise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:48&lt;/strong&gt; · You write the query and we do the rest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:51&lt;/strong&gt; · That means that all you have to do when you write the query is we&apos;ll create an API endpoint, define an O policy for that endpoint, generate uh strongly typed client SDKs in your favorite languages like Noode.js, Dart, Cotlin, and Swift, and it will also create tool definitions so you can pass those queries directly to your LLM that you&apos;re using. This significantly accelerates app development and reduces time to value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:18&lt;/strong&gt; · We also recently supported uh releasing Firebase data connect to go to GA at cloud next and we&apos;ve also added two things that people have been really excited about. One is advanced query improvements so you can see really nice insights into the sophisticated data retrieval in transactions from additional server values and multi-step capabilities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:38&lt;/strong&gt; · We&apos;ve also introduced a schema and query generation agent which is really amazing for when you&apos;re wanting to build your application and you give it a list of the models that you&apos;re trying to build and we&apos;ll generate the schema for you. And this is also available inside Gemini code assist and VS code extensions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:57&lt;/strong&gt; · Next, let&apos;s talk about Firebase app hosting. Firebase app hosting is an opinionated serverless hosting solution for modern full stack frameworks like Nex.js. These fullstack frameworks make it have simpler to get the benefits of serverside rendering so you can have improved SEO and faster load times with the ease and familiarity of JavaScript.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:17&lt;/strong&gt; · But this means that deploying the web isn&apos;t as simple as throwing up a bunch of static files on a CDN. You also need a back-end server. You need serverside rendering a build pipeline for the static generation. And of course, you also need a CDN for the caching. There&apos;s not a single part of this that you can do without, but with Firebase hosting, we&apos;re here to make the entire stack simpler for us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:38&lt;/strong&gt; · App hosting seamlessly integrates with the deployments from GitHub to Google Cloud, so it sets up all the necessary cloud services and detects what you need in your source code. App hosting automatically builds the assets in Cloud Build, deploys the dynamic content to Cloud Run, and serves the cached assets from Cloud CDN. You push your changes to GitHub and we take care of the rest with Firebase app hosting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:01&lt;/strong&gt; · We also launched app hosting to GA at Cloud Next, but there&apos;s so much more that we&apos;ve added. We&apos;ve also launched support for deploying from local using the Firebase CLI, so you don&apos;t have to push your code to GitHub. We also now have Terraform support so that you can deploy with any custom images and we&apos;ll have and that&apos;s especially a big ask for our really big customers. And of course, we also have automatic configuration of the Firebase SDKs using the emulator suite. So now, if you&apos;re using Firebase emulators, app hosting just works out of the box.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:35&lt;/strong&gt; · Firebase app hosting also seamlessly integrates with Firebase Studio, which we&apos;ll talk about in a second, so you can easily deploy your prototypes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:44&lt;/strong&gt; · The model context protocol, or MCP, is an open standard designed to enhance the capabilities of AI agents. Raise your hand if you&apos;ve used MCP or heard of it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:55&lt;/strong&gt; · Awesome. About half the room.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:58&lt;/strong&gt; · You can kind of think about MCP like a universal remote or USB for AI applications. It&apos;s like standardizing the way that they connect and inter interact with the outside world and external data and tools and services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:10&lt;/strong&gt; · Now with MCP support coming from Firebase, you can use it in the CLI to access the full power of the Firebase services with your favorite agencies inside of Firebase Studio 2.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:22&lt;/strong&gt; · We launched Firebase Studio at Cloud Next this year and it should look very familiar to a lot of you. That&apos;s because we were here at Nerdella 2024 and we showed off project idx and this is the evolution into Firebase Studio which is deeply integrated into the Firebase platform.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:40&lt;/strong&gt; · Now let&apos;s talk about green field applications. When we have an idea for an application that solves a specific task or workflow, we need to do two one of two things. One, we can build it ourselves and figure out the framework that we&apos;re going to use, a deployment strategy or services. Or we find an engineer to take time from what they&apos;re doing and build this prototype for us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:02&lt;/strong&gt; · But now we have a third option, and that&apos;s to be able to generate a prototype from scratch without installing anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:09&lt;/strong&gt; · We can launch Firebase Studio and give it an idea for an application using the app prototyping agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:16&lt;/strong&gt; · In just a few moments, we will see a blueprint of everything that is in the app that it should build. We can take this time to refine it as needed, provide it more context and about the idea, remove features that and we do not need and after we review and ideulate, we can hit generate. Firebase Studio builds a full stack next.js XJS application that uses Tailwind for CSS and Genkit for AI features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:42&lt;/strong&gt; · You do not have to install anything since Firebase Studio is built on top of Google Cloud Workstations and Nyx OS. We have a single file called dev.nix that we will provision a machine for you and manage all the dependencies for any framework that you choose. Nyx OS is especially cool because it&apos;s backed by a massive amount of packages over 100 or 120,000 packages for Linux and you can define them in a single file and it&apos;s kind of like Docker but for your operating system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:11&lt;/strong&gt; · After we see our app running, we can interact it with a built-in preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:16&lt;/strong&gt; · This even has the option to share via QR code so that anyone with a link can access it on local host running on the VM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:25&lt;/strong&gt; · This is not intended for serving, but it makes it easy to get started and get quick feedbacks on prototypes that you&apos;re not ready to deploy yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:35&lt;/strong&gt; · With applications that require AI features, Firebase Studio will help you generate an Gemini API key. You don&apos;t even have to leave the console and we&apos;ll take care of everything for you by giving you the correct APIs in the Google Cloud console. And we&apos;ll even create a Firebase project for you if you don&apos;t have one already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:52&lt;/strong&gt; · But what if we want to connect to a real database or authenticate the user?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:58&lt;/strong&gt; · This is something that people have been asking about for a while. Well, we recently launched support for connecting to Firebase authentication and fire store right in the chat. We can prompt it to add some features that will require a database or O and we&apos;ll provision the resources, set up the rules, and modify any existing screens for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:18&lt;/strong&gt; · Firebase makes it easy to move beyond the prototypes and connect to real data sources so that you can move quickly with confidence. We&apos;re working on so many other integrations and we&apos;d love to get your feedback. This is available to everyone now. So definitely go ahead and try to get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:37&lt;/strong&gt; · When you&apos;re ready to deploy and share your prototype with the world, you can hit publish and we will roll out the application to Firebase app hosting for you. So anyone with a link can now access the app that you just built.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:51&lt;/strong&gt; · But what if you&apos;re wanting to use a specific stack? Oftent time people come up to us and they&apos;re like, I don&apos;t want to use Nex.js, but I want to use a specific framework that I love or I want to customize it. Well, Firebase Studio has a lot of templates to get started with. And we recently updated over 10 templates in Firebase Studio to make it easier to work with Agent tooling. We added support for the Firebase MCP server, added rules and files, as well as building a new default workspace layout that let you get started building with Gemini right away.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:21&lt;/strong&gt; · The built-in model in Gemini and Firebase now uses Gemini 2.5 Pro, and this is also something people were really excited about. This will greatly improve the quality of the apps being built as well as the issues being debugged.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:38&lt;/strong&gt; · There are many more templates to check out and if we&apos;re missing any, please submit a PR to our open- source templates on GitHub.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:49&lt;/strong&gt; · Now, let&apos;s uh now let&apos;s build an app with the Angular template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:54&lt;/strong&gt; · This is one that we recently updated and this template will also include the new Angular MCP server and it will improve the the chat experience in Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:05&lt;/strong&gt; · With the Angular template open, we can start describing what we want to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:09&lt;/strong&gt; · Let me see if I can get this started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:16&lt;/strong&gt; · This demo is not running, but um similar to the prototyper, similar to the prototyper, it&apos;ll come up with a blueprint and a PRD that&apos;ll generate a plan and start building out the application. But we&apos;ll go over a Flutter one in a second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:34&lt;/strong&gt; · If you prefer the Gemini CLI, which raise your hand if you&apos;ve used the Gemini CLI before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:39&lt;/strong&gt; · Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:41&lt;/strong&gt; · It&apos;s an amazing tool and you can open up the terminal and we have it pre-installed for you. All you have to do is log in. The AI rules I mentioned will work right away with Gemini CLI and I personally love using them both together as they can help in different use cases. One of the use cases I like to use Gemini CLI for in Firebase Studio is for search grounding and URL context to research topics and save them as markdown files for context in Gemini and Firebase to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:11&lt;/strong&gt; · But what if we need a mobile application and the web will not fit the use case that we&apos;re looking for? Well, Firebase Studio has both Flutter and React Native templates that we can use to build mobile applications without having to set up any SDKs locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:26&lt;/strong&gt; · We can go to the recently updated Flutter template and create a new project. This will launch in the same way as the Angular template and we&apos;ll now have access to the Dart MCP server that we can leverage in Gemini and Firebase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:39&lt;/strong&gt; · This makes tasks like formatting and checking for errors and debugging layout issues a lot easier with Gemini. Because the MCP server is already set up for you, you have the tools ready for you in the chat anytime with Firebase and Flutter MCP ready to go. And just like the Angular template, we can prompt Gemini to build an app and it&apos;ll create all the files that we need. One of the useful tools that I like in the Dart MCP server is searching pub.dev and asking for routing and state management packages. It will search pub.dev and then add them to the project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:10&lt;/strong&gt; · When the app is done, you will see a real Android emulator running in Firebase Studio as a second preview option.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:22&lt;/strong&gt; · We want to meet you where you are and provide AI assistance, inference or agents wherever you need. Firebase is full stack Google for the AI era and we cannot wait to see what we&apos;re going to build. Thanks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:35&lt;/strong&gt; · [Applause] Thank you so much. You can stand here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:46&lt;/strong&gt; · We can make some questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:48&lt;/strong&gt; · Sounds good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:48&lt;/strong&gt; · If you want it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:01&lt;/strong&gt; · address.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:11&lt;/strong&gt; · No.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:19&lt;/strong&gt; · No worries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:28&lt;/strong&gt; · Hi everybody. Uh thank you for being here. Um let&apos;s say I&apos;m a boost trouble founder and like alone or a little team. So I want to validate a PC or MVP and let&apos;s say I want to use favor studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:43&lt;/strong&gt; · Should I focus my energy uh to in order to do that on which skill would you say like prompt engineer, UX, UX research or should I try learning essentials to become a developer?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:58&lt;/strong&gt; · You know, I would say one of the things that&apos;s really important in this AI era for building um is UX because it&apos;s the intersection between both building and UI. uh if you are generating experiences, they don&apos;t really matter if someone can&apos;t use them. So, making sure that you focus on what is the actual end user uh experiencing as they&apos;re building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:19&lt;/strong&gt; · I&apos;m not uh I&apos;m not a technical person.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:23&lt;/strong&gt; · So, would you say like uh should I invest my energy in UX entirely?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:29&lt;/strong&gt; · So, UX is in between development and just designing. So it&apos;s when you&apos;re using AI to just build prototypes, you don&apos;t have to be that technical, but rather it&apos;s about understanding the problem and what you&apos;re trying to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:45&lt;/strong&gt; · So you&apos;ll work with PMs and teams on seeing what the customer user journeys are. And you&apos;ll work with engineering to see what is trying to be built and what needs to be tested and a and a really good UX can come up with ways that we can improve the experience uh a lot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:02&lt;/strong&gt; · Okay, got it. Thank you so much, man.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:04&lt;/strong&gt; · Other questions? I want have a question and if is it possible to deploy the front and back end and database like all together with a configuration already like done with a template?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:23&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:23&lt;/strong&gt; · So Firebase Studio like I said has dev.nix and one of the cool things about Nyx OS is it&apos;s a full Linux machine. Uh we have many things ready to get set up such as Docker which you can add with one line. You can use Kubernetes. Um something that I&apos;ve recently explored and is really popular is like caddy uh like caddy files for reverse proxy as well as like traffic. Um all you have to do in Firebase Studio which we have templates for is set up your different services on different ports. Uh and in the sidebar you can even expose those for testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:55&lt;/strong&gt; · And when you&apos;re ready to go to production, all you have to do is set up a routing template. So you can say, I want the front end to go to this URL and the back end to go to this URL. And you can deploy it to cloud run or any other places including Firebase app hosting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:10&lt;/strong&gt; · Okay. Thank you. And like if I have an already like an existed application and I want to integrate with Firebase, how we see it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:22&lt;/strong&gt; · So uh we also offer a really good import for GitHub. GitHub, Bitbucket, and GitLab. And when you import that, you can create a dev.nix file. Um, if you don&apos;t have Firebase in the source code, but you do have Firebase um, like as a service that you want to set up, you can use Gemini in Firebase to ask it. It&apos;ll have the Firebase MCPS server now, so it can help provision you and walk you through that. If you already have a Firebase project and you import it, all you have to do is Firebase login and Firebase init, and it&apos;ll show an account picker that you can choose your project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:55&lt;/strong&gt; · Okay, thank you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:58&lt;/strong&gt; · Hi, how are you? Um, I came here last year and I won a IDX t-shirt. So, it&apos;s quite unique, isn&apos;t it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:05&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:05&lt;/strong&gt; · Well, the question is like um the team at Fire Race Studio was all the time making IDX or it was like a side project and that stuff and then merged into Fire Race Studio that now we know they merge. Um, as with many things at Google, you know, uh, sometimes we work on things and we&apos;re not sure how they&apos;ll manifest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:28&lt;/strong&gt; · And idx was why it was called project idx was a preview that we wanted to build out a technology to get feedback from users. You know, one of the things that idx aimed to solve was enabling computing for many people so that even if you just had a Chromebook or a web browser or a phone, you could still access a full version of VS Code and have all the services like a built-in preview for you. Um, with Firebase, we wanted to and heard from a lot of people that they wanted to have deeper integrations for um, using storage or O connecting to a database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:02&lt;/strong&gt; · And by having it all under one brand really helped unify the teams and to move quicker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:09&lt;/strong&gt; · Thank you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:10&lt;/strong&gt; · Terrific.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:12&lt;/strong&gt; · Also, I understand that Firebase Studio has a lot of integrations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:15&lt;/strong&gt; · Yes, lots. Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:17&lt;/strong&gt; · So that makes easier to create full applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:20&lt;/strong&gt; · Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:22&lt;/strong&gt; · You have another question there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:26&lt;/strong&gt; · Hello. I have a question. Erh, what kind of things you can do with Unity?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:35&lt;/strong&gt; · Uh, with Unity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:37&lt;/strong&gt; · Yes, I see Unity in the presentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:41&lt;/strong&gt; · So, Firebase supports Unity in our SDK for Android XR and a bunch of other uh efforts around that. Firebase Studio uh doesn&apos;t support Unity um because it u just runs um uh web applications and desktop. But uh if you&apos;re wanting to use Unity, you can still do it locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:01&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:01&lt;/strong&gt; · Uh with this agent, with MCP, you can create anything more different knowations or anything more. Yeah, the MCP server basically enables a bunch of features that you would have to do in the console. So like setting up a database and configuring rules, creating new resources, and now you can just stay in your favorite agent and it&apos;ll do that for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:41&lt;/strong&gt; · Fire.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:49&lt;/strong&gt; · Okay. If I raise to your support okay good any other question around hello nice what advice will you give to people to start using fire studio in projects that are start are right now in production or had so many hers um so One of the things that we&apos;re seeing a lot of people build with is uh a couple different things. One, people love to build games in Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:26&lt;/strong&gt; · So, if you have something that you&apos;ve been wanting to try and create, it&apos;s a great spot to give a first draft. Um, people with a data science background are really liking Firebase Studio because they can upload documents and create these one-off workflow uh tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:40&lt;/strong&gt; · Um, and then also people are using Firebase Studio for like staging environments because you can import uh your code into this VM. You can set up Postgress. You can set up all of your features. And because of our really cool sharing and collaboration features, you can share that with other people at the company, all be connecting to it at the same time. And you can even have your mobile applications running locally connect to the the staging VM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:08&lt;/strong&gt; · I have another question if you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:10&lt;/strong&gt; · Looking at the Firebase road map, what is the next big step of AI power experience for developers? Uh so as we shared earlier Firebase AI logic is something that uh we are investing a lot in and right now we&apos;re looking for the feedback because we are building out this SDK to make as easy as possible to bring it to as many platforms as possible like in the languages you write.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:33&lt;/strong&gt; · Perfect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:37&lt;/strong&gt; · Nice there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:44&lt;/strong&gt; · Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:46&lt;/strong&gt; · Hello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:48&lt;/strong&gt; · Um, I want to know how you can deploy an an application because I know how this works for front end and back end. That&apos;s is clear for me. But sometime when you needs to deploy a application, maybe you need a a Google account, a account. So I don&apos;t know if exists an integration for for the deploy at production for Google Play you mean like Android?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:20&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:21&lt;/strong&gt; · Um so we do support uh Flutter and React Native. Um outside of like just being able to build the APK and the container and upload it to resources. You can totally sign the app bundle uh all built into the VM. Uh so you wouldn&apos;t have to do anything outside of that other than open up Google Play, open up Firebase Studio and just download and upload the files. Uh and then you can also use a variety of like thirdparty services that connect to GitHub and you can push your changes and automatically deploy through CI pipelines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:54&lt;/strong&gt; · We have another question uh in the chat. You want to read it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:01&lt;/strong&gt; · Uh you can go for it. I will get fired.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:04&lt;/strong&gt; · I&apos;m not sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:06&lt;/strong&gt; · Being a dev, I use V0 and the versels AI SDK daily. What do you think in this ma is the main advantage of switching to Firebase Studio and Genkit?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:19&lt;/strong&gt; · Um, so first of all, Firebase Studio is a VM that you own. So you can use all the things that you would normally inside of Firebase Studio. So if you connect to Verscell SDKs or you decide to deploy outside of Firebase, we just want to make it as easy as possible to integrate. Um, but one of the things that Genkit offers is the open telemetry to be able to connect to all of the monitoring. Uh, we&apos;ve seen people use it a lot for evals to be able to see in production um, are my prompts working?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:48&lt;/strong&gt; · And that&apos;s where things like Firebase remote config come in really handy. So you can dynamically change the model that you&apos;re using in production. You can change the prompts if you&apos;re seeing that there&apos;s like um starting to see a lot of abuse and you want to lock it down. You can do that without having to deploy an update.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:03&lt;/strong&gt; · Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:04&lt;/strong&gt; · Yeah. And another question, do you have access to any specific models when when you use Firebase Studio? You have different quotas or tiers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:12&lt;/strong&gt; · Yeah. So, uh everybody for free gets uh three uh workstations and we have a very generous Gemini 2.5 quota u very similar to Gemini CLI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:22&lt;/strong&gt; · Okay. So if you you Okay. Do you have a question?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:26&lt;/strong&gt; · Uh yeah, here. Um so I&apos;m asking out of ignorance because I&apos;ve never used Firebase before. Um is there a way to change the subdomain once you deploy an application to production? Because I&apos;m guessing that when you deploy you get an URL with Firebase, like a subdomain. Is there a way to bring your own subdomain?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:46&lt;/strong&gt; · Yep, you sure can. Uh, one of the cool things about app hosting and the regular Firebase hosting is not only can you connect any custom domain, you can also do multiple subdomains. So, you can even have different domains for preview ser uh branches or different micro apps if you have like a monolith.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:02&lt;/strong&gt; · Okay, super cool. Thank you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:05&lt;/strong&gt; · Good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:06&lt;/strong&gt; · Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:08&lt;/strong&gt; · Hi, Rodie. Very nice presentation. My question is not technical. uh the company I work for is pretty much involved or locked in I would say with uh Microsoft solutions and Ashure. What would you say I I can say to them in order to take a look on this or consider this this this solution?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:28&lt;/strong&gt; · Um so one of the cool things about Firebase Studio and um the previous tool idx is we&apos;re just trying to make it as easy as possible for people to onboard.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:37&lt;/strong&gt; · We hope that if you brought in your company&apos;s code and created a template that people could onboard writing code right away within minutes instead of hours or days like it normally would take. Um so right away like people getting into it are not familiar with their codebase can already start to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:52&lt;/strong&gt; · Uh the other thing is it&apos;s really good for um ramping up to new products or like if someone wants to integrate a new feature or library or drastically change what&apos;s needed if you&apos;re going from Python 2 to 3 or completely switching to Go and a Rust backend, all they have to do is update the dev.nix file and nobody has to like install anything locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:16&lt;/strong&gt; · Asa, hello. Thank you for the presentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:19&lt;/strong&gt; · Uh this is regarding MCP. Yes, we support um Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:24&lt;/strong&gt; · I need to go again. Yeah. Hello. Thank you for the presentation. This is regarding MCP. Uh I don&apos;t want to put you on the spot. Uh but regarding the I just wanted to know which are all the native features supported by the MCP that are allowed at this point. I mean it is only tools. It supports also prompts resources or or the other ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:45&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:45&lt;/strong&gt; · So Firebase Studio uh I believe supports tools. Uh and then Gemini uh CLI inside of Firebase Studio uh supports a lot more. There&apos;s also Genkit in Firebase Studio which also can read and be an MCP server and it also supports uh a lot of the uh the different aspects of MCP2.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:06&lt;/strong&gt; · Okay. Do we have any other question?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:09&lt;/strong&gt; · Guess I have more questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:11&lt;/strong&gt; · Yeah, me too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:13&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:13&lt;/strong&gt; · For example, the first time I saw Firebase Studio, it was like it&apos;s a VS Code in the cloud, right? But when I saw the demo in the stand yesterday, I saw that you have like a lot of new things, integration stuff. What are like the top three things that you can do in um Firebase Studio that you can do easily on a local development?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:33&lt;/strong&gt; · Yeah, I think one of the things uh like I said that people may not realize is just how easy it is to get a database up and running or get these like really weird obscure uh examples that you find on GitHub. Um you can use Gemini to help you like figure out, oh, this is not installed and it&apos;ll go and search and get it updated for you. Um, we also have some really cool things like the um setting up the Firebase services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:00&lt;/strong&gt; · Uh, because we&apos;ve done the work to integrate it so deeply into Firebase, uh, we will go ahead and do like 10 steps that you would have to do by hand all just behind the scenes like clicking for you. So, if you&apos;re just in that universe and you want to use us, uh, we were going to try to make it as easy as possible. Um, and then even simple things like creating a Gemini API key usually takes like a couple clicks or if you&apos;re having to go into AI studio and grab it from different places, we keep it all in one place uh for you ready to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:30&lt;/strong&gt; · Nice. And when you are using the MCP server for Firebase, you can also query information about your database inside. So something I usually do is go to the Firebase um like dashboard and go to the database try to query.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:45&lt;/strong&gt; · You can just ask it in the chat now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:46&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:47&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:47&lt;/strong&gt; · how many users are in my application and you will get yeah that&apos;s quite nice perfect any other question I have the last one if you want it oh sorry hello my name is Timur I&apos;m a data engineer and primarily firebase is a as a tool for software engineer what about uh Data engineering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:18&lt;/strong&gt; · Can we data engineers leverage uh this tool for our daily tasks?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:24&lt;/strong&gt; · Oh yeah. Uh one of the cool things about Firebase Studio specifically is like you don&apos;t even have to use Firebase if you don&apos;t want to. You can open it up and create like a data lakeink with Duct DB or connect to BigQuery. Um a lot of people use it to throw in a bunch of different connections and create little dashboards to be able to visualize the data. So yeah, that&apos;s absolutely something that we saw really early.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:45&lt;/strong&gt; · People are excited about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:48&lt;/strong&gt; · Nice. And the last one if you want it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:51&lt;/strong&gt; · Sounds good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:51&lt;/strong&gt; · What is the AI power experience trend that you are most excited about right now? There are any application or features being built with Firebase that you consider revolutionary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:04&lt;/strong&gt; · I still feel like uh Gemini Nano Banana is still really popular and people building some awesome stuff with it. Yeah. And I think uh that&apos;s been an explosion of like creativity that has been really cool to see especially when it comes to AI logic or genkit. People are just asking to generate images and that seems to be something that a lot of people want to do in our products.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:27&lt;/strong&gt; · I don&apos;t know if people try raise your hand if you try nanoano.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:33&lt;/strong&gt; · Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:33&lt;/strong&gt; · We need to see a lot more hands for sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:36&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:36&lt;/strong&gt; · No, it it&apos;s really fast. I I think it&apos;s the best thing it has compared to other models. you have like flu flux context that that it&apos;s really nice but it&apos;s not as fast as another one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:46&lt;/strong&gt; · Yeah. And it&apos;s also really easy to edit images and other things too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:49&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:50&lt;/strong&gt; · And the is so good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:51&lt;/strong&gt; · Yeah. Yeah. Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:52&lt;/strong&gt; · Really?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:53&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:53&lt;/strong&gt; · Awesome. Well, [Applause] thank you. One more time. Thank you.&lt;/p&gt;
</content:encoded></item><item><title>Building Firebase Studio Rules for Angular Live</title><link>https://rodydavis.com/videos/firebase-studio/building-firebase-studio-rules-for-angular-live/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/building-firebase-studio-rules-for-angular-live/</guid><description>Mark and Rody demonstrate building Firebase Studio rules in AI Studio to help developers improve code generation for Angular apps.</description><pubDate>Sat, 14 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Building Firebase Studio Rules for Angular Live&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;YfdsFSVyZ4I&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Join in as Mark and Rody build rules in AI Studio to help developers build Angular Apps in the Firebase Studio IDE.&lt;/p&gt;
&lt;p&gt;Try Firebase Studio: goo.gle/4e8748C&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:04&lt;/strong&gt; · MARK THOMPSON: OK, I started my introduction-- I started my introduction and I forgot to add myself to the stage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:12&lt;/strong&gt; · [LAUGHS] Hey, everyone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · Welcome to this very special series of live streams where we&apos;re going to be working on an interesting problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:21&lt;/strong&gt; · We&apos;ve heard from lots of developers that they want to get better output from code gen tools and things like that with Angular, so I thought it could be really interesting if we started to build some of the prompts together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · And here&apos;s where you at home get to support this effort.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · During our time together, like, in the chat, put in your comments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · Put in some things that you want us to try, put in some of your frustrations or wins or things that work for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:48&lt;/strong&gt; · Because what we would love to do is create sets of prompts that can help developers everywhere build excellent apps with AI tools support.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · So this is about the code generation side of the story, and not so much about adding a feature to your application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · We will do more of those streams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04&lt;/strong&gt; · OK, I know you all love those streams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06&lt;/strong&gt; · We love doing them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07&lt;/strong&gt; · We&apos;re going to do so many more of these things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · I&apos;m just having a good time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · Let&apos;s see who&apos;s in the building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11&lt;/strong&gt; · We got people in the building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13&lt;/strong&gt; · People have been here for a while.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · OK, Jack has pulled up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16&lt;/strong&gt; · How are you, Lars?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18&lt;/strong&gt; · Hello, hello, hello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · Hello, hello, hello, Kodoku169.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24&lt;/strong&gt; · What&apos;s going on?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25&lt;/strong&gt; · Dev Junior, what&apos;s up?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27&lt;/strong&gt; · Hello, friends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28&lt;/strong&gt; · Oh, the dab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28&lt;/strong&gt; · Brrap bap bap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29&lt;/strong&gt; · All right, sounds good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31&lt;/strong&gt; · Hello, RSD Journey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33&lt;/strong&gt; · What&apos;s going on?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34&lt;/strong&gt; · Kamlesh, hello from India.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:36&lt;/strong&gt; · Hey, I send lots of love your way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · Michael Small in the building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:38&lt;/strong&gt; · What&apos;s up, Michael Small?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:41&lt;/strong&gt; · Sudhansu, hello, hello, hello, hello, hello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45&lt;/strong&gt; · Kapsel, hello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45&lt;/strong&gt; · How are you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46&lt;/strong&gt; · Hello, hello, hello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:49&lt;/strong&gt; · OK, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:50&lt;/strong&gt; · Merhaba, from Egypt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:52&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53&lt;/strong&gt; · All right, let&apos;s get Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · I want to make sure Rody is going to be in the building soon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · So we got a very special guest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57&lt;/strong&gt; · I don&apos;t know if you can tell by my attire, I am wearing a Firebase Studio shirt today, because that&apos;s the tool we&apos;re going to be using for today&apos;s session.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:06&lt;/strong&gt; · So that&apos;s very, very, very good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:08&lt;/strong&gt; · Very, very, very good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:09&lt;/strong&gt; · Hello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:09&lt;/strong&gt; · Let&apos;s see who else we got.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:10&lt;/strong&gt; · We got-- hello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:11&lt;/strong&gt; · All these friends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:12&lt;/strong&gt; · People are here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:13&lt;/strong&gt; · What&apos;s going on?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:14&lt;/strong&gt; · Hello, hello, hello from Ukraine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · What&apos;s up, what&apos;s up, what&apos;s up?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:17&lt;/strong&gt; · Hello from Chile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:18&lt;/strong&gt; · Hello, hello, hello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:19&lt;/strong&gt; · So many people in the building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:21&lt;/strong&gt; · We&apos;re so super excited.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:23&lt;/strong&gt; · All right, we&apos;re just waiting on our guest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:25&lt;/strong&gt; · Oh, can you tell my shirt?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:26&lt;/strong&gt; · Let me see, let me just model my shirt off to you friends at home for a second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:31&lt;/strong&gt; · See this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · Got my Firebase Studio shirt on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:35&lt;/strong&gt; · No, I&apos;m not joining the Firebase Studio team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:37&lt;/strong&gt; · I just have this shirt and I thought it&apos;d be fitting to wear this shirt today for this stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:45&lt;/strong&gt; · So OK, as we&apos;re waiting for our guest of honor to join us today, I would love to hear in the chat, if you&apos;re watching at home in the chat, let me know about your experiences so far using tools to generate Angular code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:59&lt;/strong&gt; · What has worked for you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:00&lt;/strong&gt; · What hasn&apos;t worked for you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:01&lt;/strong&gt; · Would love to hear from that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:03&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:04&lt;/strong&gt; · We said hello to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:05&lt;/strong&gt; · Hello, from Liechtenstein.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:07&lt;/strong&gt; · Awesome, awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:08&lt;/strong&gt; · We got people in the building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:13&lt;/strong&gt; · All right, let me have a look here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · All right, all right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:19&lt;/strong&gt; · As we&apos;re going, as we&apos;re going, as we&apos;re going.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:21&lt;/strong&gt; · So many windows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:31&lt;/strong&gt; · All right, I&apos;m sending Rody a message.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:32&lt;/strong&gt; · Let him know we&apos;re live, and the people are ready for you to join us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:43&lt;/strong&gt; · OK awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:45&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:46&lt;/strong&gt; · Let&apos;s see what people are saying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:47&lt;/strong&gt; · Oh, let&apos;s go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:48&lt;/strong&gt; · Joe in the building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:49&lt;/strong&gt; · What&apos;s up, what&apos;s up, what&apos;s up, what&apos;s up?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:51&lt;/strong&gt; · Hello, veryone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:53&lt;/strong&gt; · Listen, you should start saying that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:54&lt;/strong&gt; · That should become your call sign.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:56&lt;/strong&gt; · Hello, veryone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:57&lt;/strong&gt; · All right, we&apos;re doing good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:58&lt;/strong&gt; · All right, so people are coming in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:00&lt;/strong&gt; · It&apos;s great to have everybody here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:02&lt;/strong&gt; · We should be pretty good to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:05&lt;/strong&gt; · Our guest Rody works on the Firebase Studio team, which is so exciting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:10&lt;/strong&gt; · I&apos;m so glad that he&apos;s going to be hanging out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:13&lt;/strong&gt; · Oh, I wonder-- OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:16&lt;/strong&gt; · This will be funny.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:17&lt;/strong&gt; · Let me see something real quick.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:19&lt;/strong&gt; · Is-- OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:21&lt;/strong&gt; · Hold on one second, friends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:23&lt;/strong&gt; · One second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:23&lt;/strong&gt; · OK, this is funny.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:24&lt;/strong&gt; · He is in a meeting and waiting for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:27&lt;/strong&gt; · Give me a second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:50&lt;/strong&gt; · OK, so Rody was in the meeting waiting for me to join, so that&apos;s where he is, but he&apos;ll be right over.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:56&lt;/strong&gt; · We&apos;re going to get started very, very soon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:57&lt;/strong&gt; · Very excited.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:58&lt;/strong&gt; · &amp;quot;GitHub Copilot with Claude Sonnet 4 is pretty decent for start.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:01&lt;/strong&gt; · Gemini 2.5 Pro is great for refactoring and continued development.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:06&lt;/strong&gt; · Oh, I want to know more about this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:08&lt;/strong&gt; · I want to know a lot more about what you mean when you say refactoring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:11&lt;/strong&gt; · Well, I know what you mean by refactoring, but what kind of prompts are you using when you say continued development?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:17&lt;/strong&gt; · That&apos;s interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:18&lt;/strong&gt; · Super interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:19&lt;/strong&gt; · Resource, HTTP Resource, why?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:22&lt;/strong&gt; · I&apos;m going to answer this question while we wait.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:26&lt;/strong&gt; · Here&apos;s what&apos;s really interesting about Angular right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:29&lt;/strong&gt; · So we have been introducing our fully fledged new reactivity system that&apos;s based on signals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:36&lt;/strong&gt; · And so when you&apos;re working with some tasks it would be really nice if you can get those values exposed as signals, because here&apos;s what signals give you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:46&lt;/strong&gt; · Here&apos;s one of the big things they give you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:48&lt;/strong&gt; · You&apos;re not having to manage the changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:50&lt;/strong&gt; · It&apos;s reacting to changes in the data, and you get the latest value yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:54&lt;/strong&gt; · You&apos;re not having to dig deep into this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:56&lt;/strong&gt; · So if you think about what about an asynchronous task, like an asynchronous data fetching, and then you get the data, and then you can rerun that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:09&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:10&lt;/strong&gt; · Let me say it a different way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:11&lt;/strong&gt; · So for resource, in the case, you can base it on a signal value, let&apos;s say a user ID.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:15&lt;/strong&gt; · That user ID changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:17&lt;/strong&gt; · You don&apos;t want to have to have an event handler that you didn&apos;t have to be responsible for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:21&lt;/strong&gt; · And then, you&apos;re like, OK, well did this value change?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:23&lt;/strong&gt; · If the value changed, let me run this asynchronous event, and then let me track the value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:28&lt;/strong&gt; · You can do a different way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:29&lt;/strong&gt; · You can base your resource on a reactive value like a signal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:34&lt;/strong&gt; · And then, it&apos;ll rerun whatever asynchronous data fetching that you&apos;re doing for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:39&lt;/strong&gt; · And then, on top of that, you get nice parts of the application working for you, meaning you get things the status of the loading, and if there&apos;s an error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:49&lt;/strong&gt; · And all this exposes the signal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:50&lt;/strong&gt; · So that&apos;s why you have these things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:52&lt;/strong&gt; · The HTTP resource is just a specialized version of resource which is focused on fetching data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:58&lt;/strong&gt; · Hopefully, that&apos;s helpful to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:00&lt;/strong&gt; · Hopefully, that&apos;s helpful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:00&lt;/strong&gt; · &amp;quot;Firebase Studio is the only AI workspace that works good with Angular projects for me.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:04&lt;/strong&gt; · I love hearing that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:05&lt;/strong&gt; · I love hearing that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:06&lt;/strong&gt; · All right, I think Rody loves hearing that too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:08&lt;/strong&gt; · Rody, you came right on time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:10&lt;/strong&gt; · Somebody just said the nicest thing about Firebase Studio, oh, as I&apos;m twinning with the Firebase Studio shirt today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:17&lt;/strong&gt; · It say, &amp;quot;Firebase Studio is the only AI workspace that works good with Angular projects for me.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:22&lt;/strong&gt; · Love to hear that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:24&lt;/strong&gt; · That&apos;s really exciting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:25&lt;/strong&gt; · RODY DAVIS: Yeah, that&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:27&lt;/strong&gt; · MARK THOMPSON: All right, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:28&lt;/strong&gt; · &amp;quot;I use cursor in my Angular app.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:29&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:30&lt;/strong&gt; · Well, I want to learn more about why you use Cursor, and what&apos;s working, what&apos;s not working for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:34&lt;/strong&gt; · Because what we care about is that whatever you&apos;re building with, it&apos;s serving your needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:39&lt;/strong&gt; · That&apos;s what we care about the most.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:40&lt;/strong&gt; · Today, we&apos;re talking about Firebase Studio because we have some really great features there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:44&lt;/strong&gt; · But whatever you use, use what you use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:48&lt;/strong&gt; · Oh, here&apos;s a good thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:50&lt;/strong&gt; · Joh also comments, &amp;quot;not all data fetching happens with the HTTP client, ie SDK&apos;s.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:55&lt;/strong&gt; · That&apos;s true.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:56&lt;/strong&gt; · That is very, very, very true.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:58&lt;/strong&gt; · OK, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:59&lt;/strong&gt; · I saw a PR about httpResource docs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:01&lt;/strong&gt; · Yes, we have more docs coming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:02&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:04&lt;/strong&gt; · If you have more questions about Angular, go ahead and put them in the chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:06&lt;/strong&gt; · We can answer those separately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:08&lt;/strong&gt; · But we have a very special guest in the building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:11&lt;/strong&gt; · So I&apos;ve actually known our guest Rody for a long time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:15&lt;/strong&gt; · We both come from a different community before we joined Google, and we both ended up working on different products and not that thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · Rody, how did we meet?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:25&lt;/strong&gt; · Well, first, introduce yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:26&lt;/strong&gt; · Tell people what you work on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:27&lt;/strong&gt; · RODY DAVIS: Yeah, so my name is Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:29&lt;/strong&gt; · I am one of the DevRel leads for Firebase Studio, and kind of helping people get started with templates, working with different frameworks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:36&lt;/strong&gt; · I&apos;ve been pretty involved in the Flutter community and a variety of frameworks, and exploring a lot of different things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:42&lt;/strong&gt; · MARK THOMPSON: Yeah, fantastic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:44&lt;/strong&gt; · So now, people don&apos;t know this about us, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:47&lt;/strong&gt; · How did we actually meet though, in real life, before Google and before what we work on now?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:53&lt;/strong&gt; · RODY DAVIS: When was the first time?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:54&lt;/strong&gt; · That would have been at the, Flutter Interact?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:58&lt;/strong&gt; · MARK THOMPSON: Yeah, that&apos;s right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:59&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:00&lt;/strong&gt; · Yeah, 2019.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:01&lt;/strong&gt; · Wow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:01&lt;/strong&gt; · MARK THOMPSON: Yeah both were Flutter developers just thriving in the Flutter community, contributing, adding lots of value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:07&lt;/strong&gt; · And I think we both actually applied for the same job at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:11&lt;/strong&gt; · RODY DAVIS: Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:12&lt;/strong&gt; · [LAUGHS] Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:13&lt;/strong&gt; · Yeah, and I think it ended up being a manager role too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:15&lt;/strong&gt; · MARK THOMPSON: That&apos;s right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:17&lt;/strong&gt; · That&apos;s right, it was a manager role.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:18&lt;/strong&gt; · And I remember getting interviewed and they were just like, yeah, we like you, but not for a manager.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:23&lt;/strong&gt; · RODY DAVIS: Same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:23&lt;/strong&gt; · MARK THOMPSON: And I&apos;m like, all right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:24&lt;/strong&gt; · RODY DAVIS: Exactly, same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:25&lt;/strong&gt; · MARK THOMPSON: All right, that&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:27&lt;/strong&gt; · I&apos;m not offended by that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:28&lt;/strong&gt; · [LAUGHS] RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:30&lt;/strong&gt; · MARK THOMPSON: Oh, man.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:31&lt;/strong&gt; · How fun, how fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:32&lt;/strong&gt; · And now, here we are, both working on very different products but all both having a good time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:37&lt;/strong&gt; · All right, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:38&lt;/strong&gt; · &amp;quot;I haven&apos;t tried Angular Studio yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:40&lt;/strong&gt; · I feel a bit hesitant, but I&apos;m planning to start using it after today.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:44&lt;/strong&gt; · Angular Studio, I think you mean Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:46&lt;/strong&gt; · And speaking of Firebase Studio, here is Firebase Studio in the flesh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:50&lt;/strong&gt; · So I have it up on the screen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:53&lt;/strong&gt; · And we&apos;re going to do some stuff together, because Rody, here&apos;s what I would like to do today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:58&lt;/strong&gt; · I would like to use, I think we&apos;re calling it, like, the-- how do you describe the non-app prototype mode?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:06&lt;/strong&gt; · What&apos;s the language that you use?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:08&lt;/strong&gt; · RODY DAVIS: We&apos;ve been saying code view or just the normal IDE view.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:12&lt;/strong&gt; · We view when you&apos;re building with natural language, that&apos;s our prototype or app prototyping agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:17&lt;/strong&gt; · So everything else is just the default experience, which we really think is super awesome, and I use all the time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:25&lt;/strong&gt; · So I&apos;m excited to dive into it today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:27&lt;/strong&gt; · MARK THOMPSON: Yeah, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:28&lt;/strong&gt; · So one of the benefits of the IDE view or the code view is that you still have access to an AI assistant, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:39&lt;/strong&gt; · RODY DAVIS: Yep, mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:40&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:41&lt;/strong&gt; · RODY DAVIS: Yeah, in fact, you&apos;ll be able to choose your own model, bring your own API key.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:44&lt;/strong&gt; · You can even connect to a fine tuned model too, in the picker, which is great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:48&lt;/strong&gt; · MARK THOMPSON: Oh, excellent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:49&lt;/strong&gt; · So what I would love to do is see how can we configure in Firebase Studio a way to tell it how to generate even better Angular code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:01&lt;/strong&gt; · RODY DAVIS: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:02&lt;/strong&gt; · MARK THOMPSON: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:03&lt;/strong&gt; · Awesome So let&apos;s do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:03&lt;/strong&gt; · So I think the best thing I can-- or not the best thing, the first thing I can do is, so down where it says start coding an app-- I&apos;m going to zoom in just so people can see what I&apos;m doing down here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:12&lt;/strong&gt; · We&apos;ve got a lot of options from different products.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:16&lt;/strong&gt; · Let me see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:16&lt;/strong&gt; · Let me hide this little thing down here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:18&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:18&lt;/strong&gt; · Y&apos;all can&apos;t see it, but I can see it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:19&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:20&lt;/strong&gt; · So I&apos;m going to choose-- hey, Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:21&lt;/strong&gt; · What&apos;s going on?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:22&lt;/strong&gt; · I&apos;m going to choose Angular from this list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:23&lt;/strong&gt; · RODY DAVIS: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:24&lt;/strong&gt; · You want to also show them where the second place they can find it too?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:28&lt;/strong&gt; · MARK THOMPSON: Oh, please.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:28&lt;/strong&gt; · Let&apos;s go back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:29&lt;/strong&gt; · OK, show me, show me, show me, show me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:30&lt;/strong&gt; · RODY DAVIS: So click on New Workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:32&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:33&lt;/strong&gt; · So I&apos;m going to go down here, click on New Workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:35&lt;/strong&gt; · RODY DAVIS: Yeah, when people first come to Firebase Studio, they may not realize we have over 160 plus templates configured for a lot of different stacks and frameworks and languages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:44&lt;/strong&gt; · So if you come here, one of the first things you&apos;ll see is Angular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:47&lt;/strong&gt; · And you can even scroll down and there&apos;s a bunch of other templates that you might be used to or-- MARK THOMPSON: Oh, let&apos;s go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:53&lt;/strong&gt; · RODY DAVIS: --have tried out in the community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:54&lt;/strong&gt; · So there&apos;s many more that we&apos;re always building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:56&lt;/strong&gt; · And if you ever have feature requests or want an update, please let me know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:01&lt;/strong&gt; · And then also, we accept community templates too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:04&lt;/strong&gt; · So you could just make a PR and support your favorite framework.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:08&lt;/strong&gt; · MARK THOMPSON: Look at this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:09&lt;/strong&gt; · This is amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:10&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:10&lt;/strong&gt; · I don&apos;t know that I spent as much time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:13&lt;/strong&gt; · So before Firebase Studio and Project IDX started and became the same product, I had only used just, like, going straight into one of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:22&lt;/strong&gt; · So I didn&apos;t even see some of these things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:24&lt;/strong&gt; · This is fantastic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:25&lt;/strong&gt; · So you can just go to the-- so let me remind people how I got here before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:29&lt;/strong&gt; · RODY DAVIS: Yup, just New Workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:30&lt;/strong&gt; · MARK THOMPSON: Yeah, from New Workspace right there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:33&lt;/strong&gt; · Got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:33&lt;/strong&gt; · RODY DAVIS: That&apos;s right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:33&lt;/strong&gt; · MARK THOMPSON: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:34&lt;/strong&gt; · All right, let&apos;s pick Angular, and then let&apos;s actually get to this stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:37&lt;/strong&gt; · So I want to say this is-- I always call my projects-- I always just say, what we&apos;re doing today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:42&lt;/strong&gt; · We&apos;re going to say, ai-dev-xp.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:45&lt;/strong&gt; · So some experiments working with AI development, because I love the idea of using-- I do accept the terms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:53&lt;/strong&gt; · I love the idea of using AI assistant to help boost my work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:58&lt;/strong&gt; · I mean, I like it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:00&lt;/strong&gt; · I know some people are kind of on the fence about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:02&lt;/strong&gt; · I like it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:04&lt;/strong&gt; · RODY DAVIS: Oh yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:04&lt;/strong&gt; · So I&apos;m curious your thoughts on how you&apos;ve been able to approach using AI assistants.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:10&lt;/strong&gt; · Because I definitely have thoughts too, on being an experienced developer and trying to find ways to amplify your workflow, as opposed to slowing it down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:19&lt;/strong&gt; · MARK THOMPSON: Good question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:22&lt;/strong&gt; · So recently, I just made a multiplayer web-based game app that had three different UIs, an admin view, a player view, and a spectator view.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:35&lt;/strong&gt; · And for me, the way I was able to have it leverage things is when I knew exactly what I wanted to happen, I would start describing the actual code that I wanted in the comment, and then I will let Gemini fill in the gaps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:50&lt;/strong&gt; · But sometimes, I really wanted the ability to just have it like, not do anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:56&lt;/strong&gt; · I know what I&apos;m going to type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:57&lt;/strong&gt; · I don&apos;t even want your help right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:59&lt;/strong&gt; · I know what I&apos;m going to type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:00&lt;/strong&gt; · Don&apos;t suggest anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:01&lt;/strong&gt; · All the IntelliSense Windows, the Gemini suggestions, I want everything off for a second, so that way I can actually just type what I&apos;m going to type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:09&lt;/strong&gt; · So I&apos;ve been doing some good stuff like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:12&lt;/strong&gt; · That&apos;s been really working for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:13&lt;/strong&gt; · What about you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:14&lt;/strong&gt; · RODY DAVIS: So I feel like I have been doing what I&apos;m calling compressed or condensed RAG, where I&apos;m using Gemini Deep Research or Notebook LLM to explore a body of research or something I&apos;m trying to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:30&lt;/strong&gt; · And my goal of that is to take a huge breadth first search.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:35&lt;/strong&gt; · So I&apos;m giving it a bunch of links to documentation, I&apos;m using my deep research to do expanded search queries, and I&apos;m having it generate some kind of report.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:45&lt;/strong&gt; · So what I do is I take that report, convert it to markdown, and I include it in my code base, and then I generate a prompt based on that piece of research because it doesn&apos;t let the model have to guess about what it needs to explore.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:00&lt;/strong&gt; · I&apos;ve regularly started building with languages that is not even in the training data set, or things that are very new, because you&apos;re teaching it and guiding it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:10&lt;/strong&gt; · And the more explicit you can be about the architecture and the vision that you have for it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:15&lt;/strong&gt; · I mean, you can get really, really far.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:17&lt;/strong&gt; · And one of the things that I&apos;ve heard recently that I thought was really nice is when you&apos;re prompting a model, it&apos;s a reflection of what you&apos;re putting into it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:26&lt;/strong&gt; · So the higher quality input you put in, the higher quality output you&apos;re going to get out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:29&lt;/strong&gt; · As well as if we start to think about prompting image generators, you never are confused if you tell someone a dream with like, two sentences, and they didn&apos;t see exactly what you were seeing in your head.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:43&lt;/strong&gt; · You have to be very descriptive and elaborate to get that point across, because communication is how we actually connect and describe our intent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:52&lt;/strong&gt; · MARK THOMPSON: Wow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:53&lt;/strong&gt; · OK, so I feel like you and I should do more streams together-- RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:56&lt;/strong&gt; · MARK THOMPSON: --because I would love to see your condensed RAG in action.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:59&lt;/strong&gt; · I think the people in the community would love to see it as well-- RODY DAVIS: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:03&lt;/strong&gt; · MARK THOMPSON: --about how they can leverage this type of stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:05&lt;/strong&gt; · So you&apos;ve already committed yourself to doing more of these with us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:08&lt;/strong&gt; · RODY DAVIS: Sounds good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:09&lt;/strong&gt; · MARK THOMPSON: [LAUGHS] All right, friends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:10&lt;/strong&gt; · So while we were describing all that stuff, the cool thing that happened so far-- RODY DAVIS: Yup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:15&lt;/strong&gt; · MARK THOMPSON: And listen, I want to be super clear, friends at home.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:17&lt;/strong&gt; · Like, the Angular community, we know that you put our trust in us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:20&lt;/strong&gt; · I&apos;m not trying to sell you on Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:22&lt;/strong&gt; · Let me be super clear.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:23&lt;/strong&gt; · I&apos;m just describing the things that are happening, and we&apos;re just going to work on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:26&lt;/strong&gt; · If you like what you see, that&apos;s different.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:28&lt;/strong&gt; · But this is not like a marketing pitch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:30&lt;/strong&gt; · RODY DAVIS: Yup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:31&lt;/strong&gt; · MARK THOMPSON: But I&apos;m just going to describe what I&apos;m seeing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:33&lt;/strong&gt; · So we start the project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:35&lt;/strong&gt; · It looks like it did the NPM install for me already, and it started the server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:41&lt;/strong&gt; · And all I did was just create the thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:43&lt;/strong&gt; · I did have to install the extension manually, which is fine, but it prompted me, which is OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:50&lt;/strong&gt; · RODY DAVIS: Yeah, which we can even fix that in the future too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:53&lt;/strong&gt; · One of the cool things about if you go to the dev dot next file, under idx folder, you can set the extensions that you want installed when the Workspace is created.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:06&lt;/strong&gt; · Also, just wanted to say, Firebase Studio, for those on the stream that may not be familiar with it, it&apos;s not trying to really compete with local development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:15&lt;/strong&gt; · We see it as a parallel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:16&lt;/strong&gt; · One of the things we&apos;re trying to do is make it really easy to collaborate with people.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:21&lt;/strong&gt; · And we all know people that we run into that are new to development or maybe have a lower spec machine, or even a Chromebook or an iPad, and want to get started with something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:33&lt;/strong&gt; · Well, now, you can share a link with someone on Firebase Studio and they can explore Angular without having anything installed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:41&lt;/strong&gt; · So it&apos;s been a really cool way to teach people how to use a certain type of framework without them having to invest, and to trying to figure out how to set up their path or get NPM or node installed locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:55&lt;/strong&gt; · MARK THOMPSON: OK, there we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:56&lt;/strong&gt; · So we got that working.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:57&lt;/strong&gt; · RODY DAVIS: Right on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:58&lt;/strong&gt; · MARK THOMPSON: So as you were talking, I just want to see what would happen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:01&lt;/strong&gt; · Can we get some stuff working?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:03&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:05&lt;/strong&gt; · Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:05&lt;/strong&gt; · All right, so let&apos;s get some community comments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:09&lt;/strong&gt; · &amp;quot;Still don&apos;t use much AI in my workflow, but all these streams have been tempting to see how others use it.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:13&lt;/strong&gt; · Yeah, I mean that&apos;s how I started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:15&lt;/strong&gt; · I really was kind of like, eh, I don&apos;t know about it in my workflow, because I know what to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:19&lt;/strong&gt; · Just like you, Michael, and many of you on the stream, you already know what to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:22&lt;/strong&gt; · So that&apos;s not as interesting to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:24&lt;/strong&gt; · But what I found is how much more I can get done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:28&lt;/strong&gt; · That&apos;s been appealing to me, the amount that I can get done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:32&lt;/strong&gt; · RODY DAVIS: Yeah, and I guess I could bring up our first thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:35&lt;/strong&gt; · Would you want to click the Settings icon?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:37&lt;/strong&gt; · MARK THOMPSON: Yeah, I do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:38&lt;/strong&gt; · RODY DAVIS: Bottom left, and let&apos;s do Settings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:43&lt;/strong&gt; · MARK THOMPSON: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:44&lt;/strong&gt; · RODY DAVIS: So for developers coming into this that may not want the full AI assistant right away, we actually offer some options that I think would be already beneficial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:54&lt;/strong&gt; · So if you type in enable inline completion-- so you can actually disable the inline autocomplete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:07&lt;/strong&gt; · I know for me personally, sometimes I like to disable that and only work with it through the chat, because I want to use the built-in LSB and stuff like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:17&lt;/strong&gt; · MARK THOMPSON: Yeah, I just want to zoom in for a second so we can see that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:21&lt;/strong&gt; · Interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:22&lt;/strong&gt; · RODY DAVIS: Yeah, and another one is you can even disable code-based indexing as a global value here too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:29&lt;/strong&gt; · So just so if you&apos;re coming into Firebase Studio and you&apos;re like, I don&apos;t want the AI or I want to be very explicit about what I want to use AI for, we totally have ways to configure that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:41&lt;/strong&gt; · MARK THOMPSON: Mhm, mhm, mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:43&lt;/strong&gt; · So I may turn it off at some point, because-- RODY DAVIS: Yeah, it&apos;ll be fine for this video.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:48&lt;/strong&gt; · But just some people may not realize that you can turn it off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:51&lt;/strong&gt; · MARK THOMPSON: Did not know you could turn it off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:53&lt;/strong&gt; · Let&apos;s go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:53&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:54&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:54&lt;/strong&gt; · Let&apos;s get some more comments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:55&lt;/strong&gt; · Got people in the streets talking to us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:57&lt;/strong&gt; · Can either-- yes, this is a good question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:00&lt;/strong&gt; · Can you do a Git repo or from a local folder?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:03&lt;/strong&gt; · RODY DAVIS: Yes, you can import from GitHub, Bitbucket, GitLab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:06&lt;/strong&gt; · And if we don&apos;t support a Git provider, you can always create a blank workspace and clone it through whatever means that you connect to your source control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:14&lt;/strong&gt; · You can also download your code as a zip file or sync it back to Git.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:18&lt;/strong&gt; · So your code is yours and you can always go back and forth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:21&lt;/strong&gt; · MARK THOMPSON: What about this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:22&lt;/strong&gt; · &amp;quot;Can I import existing projects to Firebase Studio,&amp;quot; if I don&apos;t want to start from scratch?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:26&lt;/strong&gt; · RODY DAVIS: Yep, you can import any public or private Git repository, or just upload a folder as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:33&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:34&lt;/strong&gt; · All right, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:35&lt;/strong&gt; · &amp;quot;Mark, that&apos;s next level, bro.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:37&lt;/strong&gt; · Don&apos;t delete this VOD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:38&lt;/strong&gt; · That sounds awesome.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:39&lt;/strong&gt; · Whatever I said that sounded awesome, [LAUGHS] then yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:44&lt;/strong&gt; · RODY DAVIS: Yes, plus one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:46&lt;/strong&gt; · [LAUGHS] MARK THOMPSON: Yeah, right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:46&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:47&lt;/strong&gt; · We don&apos;t delete our VODs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:49&lt;/strong&gt; · So whatever it was, it should be good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:51&lt;/strong&gt; · Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:52&lt;/strong&gt; · All right, let&apos;s continue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:54&lt;/strong&gt; · So here&apos;s something that I would try to figure out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:58&lt;/strong&gt; · So if I wanted to do, and I really don&apos;t like this example, but it&apos;s the most accessible example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:04&lt;/strong&gt; · It&apos;s a to do app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:05&lt;/strong&gt; · And I know that&apos;s the generic example, but it&apos;s quite accessible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:09&lt;/strong&gt; · RODY DAVIS: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:10&lt;/strong&gt; · MARK THOMPSON: So what I want to be able to do is have the assistant generate modern, like, code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:16&lt;/strong&gt; · And I have some guidelines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:18&lt;/strong&gt; · So if I were to do something like-- all right, so I&apos;m going to have a section here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:25&lt;/strong&gt; · And then, so I&apos;ll say list of todos, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:29&lt;/strong&gt; · RODY DAVIS: Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:30&lt;/strong&gt; · MARK THOMPSON: With a checkbox.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:33&lt;/strong&gt; · Yeah, to mark.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:35&lt;/strong&gt; · So it gave me some comments, hold on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:37&lt;/strong&gt; · You could tell me how to open up my web window again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:39&lt;/strong&gt; · RODY DAVIS: Yeah, I will.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:40&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:40&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:40&lt;/strong&gt; · Because if I close it-- RODY DAVIS: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:42&lt;/strong&gt; · MARK THOMPSON: --the marker is done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:44&lt;/strong&gt; · All right, so I want this thing to fill in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:46&lt;/strong&gt; · So how can I prompt the model to get started right now to do it for me?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:51&lt;/strong&gt; · RODY DAVIS: So this is what we usually consider is like Level 1 AI assistants, where we have autocomplete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:57&lt;/strong&gt; · And I think it is generally OK for certain types of things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:02&lt;/strong&gt; · It&apos;s way better when you have a lot of existing code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:05&lt;/strong&gt; · But this, we can now start with the Level 2 version, which is where Gemini can actually read your code and make changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:16&lt;/strong&gt; · So if you clicked on Gemini in the bottom toolbar-- MARK THOMPSON: OK, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:22&lt;/strong&gt; · Bottom toolbar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:23&lt;/strong&gt; · What am I looking-- oh, down here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:24&lt;/strong&gt; · Yep, I see it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:25&lt;/strong&gt; · RODY DAVIS: So this will open up our Gemini chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:27&lt;/strong&gt; · MARK THOMPSON: Can people see that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:29&lt;/strong&gt; · Super small.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:30&lt;/strong&gt; · I wish I could zoom in a little bit more, but I&apos;m clicking on the Gemini.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:33&lt;/strong&gt; · OK, there it is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:33&lt;/strong&gt; · RODY DAVIS: It usually will launch automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:35&lt;/strong&gt; · One thing that I found to work really well is to provide pseudocode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:40&lt;/strong&gt; · And you&apos;re kind of like doing this here, but we should just be able to-- MARK THOMPSON: Ohh!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:44&lt;/strong&gt; · RODY DAVIS: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:45&lt;/strong&gt; · Because you&apos;re giving that abstract shape of where you want to head, is going to already get closer to what you&apos;re wanting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:53&lt;/strong&gt; · MARK THOMPSON: OK, I see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:54&lt;/strong&gt; · RODY DAVIS: So I have found that Gemini in Firebase is really good when you&apos;re explicit about the files that you want to update.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:00&lt;/strong&gt; · So you could say like, create a to do component, updating both app html and app js or whatever, and allow it to know which files to be explicit about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:13&lt;/strong&gt; · MARK THOMPSON: I see app.html and app.ts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:18&lt;/strong&gt; · RODY DAVIS: Or CSS as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:19&lt;/strong&gt; · Just like, hey, here&apos;s the files that I want you to update, and then start with the changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:27&lt;/strong&gt; · MARK THOMPSON: OK, update both.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:28&lt;/strong&gt; · And then, here&apos;s what I want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:30&lt;/strong&gt; · The component should-- because I almost get into test-driven development mode describing the task.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:37&lt;/strong&gt; · Like, the component should accept an input with the label of the todo, the status of the todo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:53&lt;/strong&gt; · The component should also-- what should it also do?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:59&lt;/strong&gt; · It should also display-- oh, it should also strike through the label.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:06&lt;/strong&gt; · If the todo is marked as complete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:15&lt;/strong&gt; · RODY DAVIS: Yeah, and then, you can also, just for the viewer&apos;s knowledge, click the dropdown right there, built-in Gemini model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:23&lt;/strong&gt; · This is something that Súper Chat and our chat inside of Firebase Studio can do, is where you can actually talk to different models and choose the types of experiences that you&apos;re wanting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:34&lt;/strong&gt; · Sometimes people want to try out the preview models or custom fine tuned ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:39&lt;/strong&gt; · And then also, our interactive chat allows you to have multiple chat sessions, so you can go back and see previous ones and jump back in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:47&lt;/strong&gt; · I would suggest to do a chat per task of what you&apos;re trying to do, because it allows you to go back later and to make changes in context of the stuff that you&apos;ve already done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:57&lt;/strong&gt; · So we can just use the built-in model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:00&lt;/strong&gt; · We&apos;re using Gemini 2.5.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:02&lt;/strong&gt; · MARK THOMPSON: Ohh!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:03&lt;/strong&gt; · Uh huh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:04&lt;/strong&gt; · RODY DAVIS: And so this should just be able to work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:06&lt;/strong&gt; · And then, as we make changes, we can talk about how to even improve it even further.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:12&lt;/strong&gt; · MARK THOMPSON: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:13&lt;/strong&gt; · But before we do that, let&apos;s see, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:16&lt;/strong&gt; · &amp;quot;I used such a workspace for building a small project &amp;quot;library&amp;quot; like a dynamic form builder or dynamic table.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:22&lt;/strong&gt; · Oh, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:23&lt;/strong&gt; · All right, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:24&lt;/strong&gt; · &amp;quot;Guys, I use ChatGPT to create well-written prompts and then I paste them into Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:28&lt;/strong&gt; · It generates high-quality code.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:30&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:31&lt;/strong&gt; · RODY DAVIS: Yup, that&apos;s a great tip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:32&lt;/strong&gt; · I also use Gemini Deep Research and Thinking to do the same thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:36&lt;/strong&gt; · MARK THOMPSON: Oh, fantastic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:37&lt;/strong&gt; · Oh, it was your RAG workflow, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:39&lt;/strong&gt; · No, we&apos;re going to do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:40&lt;/strong&gt; · Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:41&lt;/strong&gt; · RODY DAVIS: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:41&lt;/strong&gt; · MARK THOMPSON: Right after this stream, I&apos;m going to put something on the calendar for him to come back and show us this RAG workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:47&lt;/strong&gt; · RODY DAVIS: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:48&lt;/strong&gt; · MARK THOMPSON: Yeah, totally going to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:50&lt;/strong&gt; · No, and Alcatraz, do not be sorry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:52&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:52&lt;/strong&gt; · MARK THOMPSON: It&apos;s OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:53&lt;/strong&gt; · I&apos;m not offended that you weren&apos;t talking about something that I said.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:56&lt;/strong&gt; · [LAUGHS] That doesn&apos;t matter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:57&lt;/strong&gt; · No, I think it&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:00&lt;/strong&gt; · &amp;quot;Please use Claude.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:02&lt;/strong&gt; · RODY DAVIS: If you want to install anything else inside this VM, you own it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:07&lt;/strong&gt; · It&apos;s just like a Linux VM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:08&lt;/strong&gt; · So people come and add their own preferences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:12&lt;/strong&gt; · MARK THOMPSON: Yeah, use what feels right for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:15&lt;/strong&gt; · We&apos;re just showing you-- so when you think about, again, friends, we&apos;re still engineers, all of us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:20&lt;/strong&gt; · And I mean people at home too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:21&lt;/strong&gt; · You&apos;re still an engineer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:22&lt;/strong&gt; · So if you think about the way you develop as a system of components working together, you can swap out the components that make the most sense for you at any point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:29&lt;/strong&gt; · And the way that this is working, you can choose your model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:33&lt;/strong&gt; · Totally, totally possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:35&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:37&lt;/strong&gt; · OK, let&apos;s do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:37&lt;/strong&gt; · Let&apos;s do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:38&lt;/strong&gt; · Let&apos;s do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:38&lt;/strong&gt; · Let&apos;s do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:39&lt;/strong&gt; · RODY DAVIS: And also, you can deploy to other places too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:40&lt;/strong&gt; · We&apos;re just trying to make it as easy as possible to deploy to Firebase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:44&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:46&lt;/strong&gt; · RODY DAVIS: So one of the first things that&apos;s going to do is read your file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:50&lt;/strong&gt; · So if you scroll up a little bit, it&apos;ll-- well, it did right before, it showed it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:56&lt;/strong&gt; · But it&apos;ll read the files as context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:58&lt;/strong&gt; · And so now, it&apos;s going to start to make changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:01&lt;/strong&gt; · You can review changes or add automatically-- MARK THOMPSON: Yeah, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:05&lt;/strong&gt; · RODY DAVIS: --but they will show the suggested change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:09&lt;/strong&gt; · So this is for todos.ts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:11&lt;/strong&gt; · MARK THOMPSON: All right, and I already see some things that I wanted to do different.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:16&lt;/strong&gt; · RODY DAVIS: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:17&lt;/strong&gt; · So we could follow up in the chat and you could tell it to do something different too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:21&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:21&lt;/strong&gt; · And is there any way that I can do it before we get to the chat level, or-- RODY DAVIS: Yeah, we can customize the AI rules if you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:29&lt;/strong&gt; · MARK THOMPSON: Yeah, let&apos;s do the AI rules, because I kind of wanted to always know to do a certain set of things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:35&lt;/strong&gt; · RODY DAVIS: Let&apos;s do that, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:36&lt;/strong&gt; · This at least shows us that this is how you get to the generation mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:40&lt;/strong&gt; · So let&apos;s go ahead and hit code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:41&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:42&lt;/strong&gt; · RODY DAVIS: All right, so before I open it up, let me talk through these changes with some of our friends at home.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:47&lt;/strong&gt; · RODY DAVIS: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:47&lt;/strong&gt; · MARK THOMPSON: The things that we care about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:49&lt;/strong&gt; · So here&apos;s a perfect example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:50&lt;/strong&gt; · If you&apos;re generating code right now, it&apos;s kind of common that you might see something like a standalone true, which is modern Angular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:57&lt;/strong&gt; · Except for in the recent versions, we&apos;ve made the standalone component declaration optional.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:02&lt;/strong&gt; · RODY DAVIS: Which is awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:03&lt;/strong&gt; · MARK THOMPSON: So you don&apos;t even have to put it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:04&lt;/strong&gt; · Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:05&lt;/strong&gt; · But then these, we want to use signals instead of the decorator base.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:09&lt;/strong&gt; · RODY DAVIS: Got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:10&lt;/strong&gt; · OK, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:11&lt;/strong&gt; · That sounds great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:12&lt;/strong&gt; · OK, one thing I would suggest is to pull up some of the code snippets that we want to use, and then we will go to the idx folder, and we&apos;re going to create a new file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:25&lt;/strong&gt; · So you can actually hit close on those changes if you want, or we can just add something new.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:33&lt;/strong&gt; · MARK THOMPSON: Let me hit close on these for now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:35&lt;/strong&gt; · RODY DAVIS: Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:35&lt;/strong&gt; · MARK THOMPSON: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:36&lt;/strong&gt; · And then, do I need to do anything here with the Gemini tab?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:39&lt;/strong&gt; · RODY DAVIS: You can just close the Gemini tab for right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:40&lt;/strong&gt; · Yeah, you can actually hit Stop on the bottom right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:42&lt;/strong&gt; · MARK THOMPSON: That&apos;s what I was looking for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:43&lt;/strong&gt; · Yeah, it was like, I saw it doing something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:45&lt;/strong&gt; · I was like, what are you doing?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:46&lt;/strong&gt; · Stop thinking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:47&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:47&lt;/strong&gt; · All right, let&apos;s just go ahead and close Gemini.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:49&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:50&lt;/strong&gt; · OK, so we&apos;re going to create a new file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:54&lt;/strong&gt; · MARK THOMPSON: All right, so in the idx folder?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:56&lt;/strong&gt; · RODY DAVIS: Yep, and it&apos;s going to be .md.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:00&lt;/strong&gt; · MARK THOMPSON: Oh, I did something over here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:02&lt;/strong&gt; · RODY DAVIS: All lowercase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:03&lt;/strong&gt; · MARK THOMPSON: Hold on, I am using a computer that I don&apos;t know how to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:06&lt;/strong&gt; · Wait a minute.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:07&lt;/strong&gt; · How do youl use the computer?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:07&lt;/strong&gt; · See, every time I try to right click, I&apos;m doing the wrong thing here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:10&lt;/strong&gt; · There we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:11&lt;/strong&gt; · New file, and then airules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:14&lt;/strong&gt; · RODY DAVIS: Yeah, one word, .md.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:16&lt;/strong&gt; · MARK THOMPSON: OK, Rody, tell us about this file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:18&lt;/strong&gt; · RODY DAVIS: Yeah, so AI rules, and we&apos;ve even supported other formats like cursor rules and other stuff too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:25&lt;/strong&gt; · But AI rules is Firebase Studio&apos;s take on how you can customize your chat and your Workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:32&lt;/strong&gt; · So one of the cool things about using these rules files is it allows you to ground the model with context about your preferences and your preferences in conjunction with this project Because it&apos;s checked into version control, other people have the same rules when they load it up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:53&lt;/strong&gt; · But yeah, so that&apos;s at a high level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:56&lt;/strong&gt; · The other cool thing you can do is give it links inside of here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:00&lt;/strong&gt; · So you could give it the Google style guide on something, or giving it a context on-- MARK THOMPSON: What?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:06&lt;/strong&gt; · RODY DAVIS: --how to do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:06&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:07&lt;/strong&gt; · One of the cool things about Gemini is it uses URL context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:10&lt;/strong&gt; · So you can parse URLs as more grounding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:17&lt;/strong&gt; · And so that&apos;s kind of a cool way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:20&lt;/strong&gt; · If it&apos;s not working, it&apos;s fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:23&lt;/strong&gt; · We&apos;ll have another way to work with it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:24&lt;/strong&gt; · But at least that&apos;s what our docs talk about, and at least what we&apos;re aiming for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:30&lt;/strong&gt; · So one of the cool things that we can do is you can maybe give it a persona.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:36&lt;/strong&gt; · So we&apos;ll use default markdown headings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:38&lt;/strong&gt; · So you can do basically, an H1, and you can say persona.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:43&lt;/strong&gt; · And then, under that, we can write a paragraph.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:45&lt;/strong&gt; · And this is where you can say-- the classic example&apos;s talk like a pirate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:50&lt;/strong&gt; · MARK THOMPSON: [LAUGHS] RODY DAVIS: But here, you want to say you are a seasoned web developer that writes modern CSS, and you prefer-- or like, we&apos;re an Angular developer that&apos;s working, using basically, the latest&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:08&lt;/strong&gt; · versions of the library, which include signals and blah, blah, blah, which kind of gives it a hint to the model to pick and generate something that&apos;s going to be from at least a more recent data set, or it put it into the same context space of where we&apos;re wanting to head.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:29&lt;/strong&gt; · So yeah, I&apos;ll defer to you on this persona.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:33&lt;/strong&gt; · MARK THOMPSON: Yeah, so while you were talking, I was trying to do what somebody in the chat said, which I thought was super cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:40&lt;/strong&gt; · I was like, all right, can I use Gemini to help me generate the persona text?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:45&lt;/strong&gt; · RODY DAVIS: Yeah, of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:46&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:46&lt;/strong&gt; · And I was like, can I do it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:48&lt;/strong&gt; · I don&apos;t know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:49&lt;/strong&gt; · OK, but first, let me see if I can-- so I&apos;m going to grab a window that&apos;s off screen just because I don&apos;t want to show my corp tabs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:58&lt;/strong&gt; · RODY DAVIS: [LAUGHS] Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:59&lt;/strong&gt; · MARK THOMPSON: Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:01&lt;/strong&gt; · So I&apos;m saying, can you help me write a persona for an Angular developer?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:07&lt;/strong&gt; · RODY DAVIS: And we also have an ask mode for Gemini too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:10&lt;/strong&gt; · But yeah, I usually use the Gemini tool as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:14&lt;/strong&gt; · MARK THOMPSON: Yeah, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:15&lt;/strong&gt; · So let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:16&lt;/strong&gt; · Can you help me write a persona for an Angular developer that uses-- OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:19&lt;/strong&gt; · To be this persona-- oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:24&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:25&lt;/strong&gt; · Write persona paragraph-- you gotta be super specific.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:28&lt;/strong&gt; · Because, like you said, when you mentioned the whole photo thing or the dream thing of like, you described it in two sentences and then you&apos;re surprised that it&apos;s not the output that you want, that really spoke to me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:39&lt;/strong&gt; · I can&apos;t lie to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:40&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:41&lt;/strong&gt; · MARK THOMPSON: I was like, that&apos;s really good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:42&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:42&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:43&lt;/strong&gt; · MARK THOMPSON: So I&apos;m being more literal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:45&lt;/strong&gt; · Or almost like when I&apos;m talking to my son.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:48&lt;/strong&gt; · If I&apos;m like, hey, keep your eye on the ball, and then he puts his little eye on the ball, I can&apos;t be surprised if he didn&apos;t get the full context from one sentence, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:55&lt;/strong&gt; · RODY DAVIS: Yeah, and so the persona, along that same analogy, is like us giving a screenplay actor role to someone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:05&lt;/strong&gt; · And if you don&apos;t give any direction, a person has the freedom to do whatever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:12&lt;/strong&gt; · But if you have a very specific goal in mind for what you want the actor to do, that&apos;s what the persona is for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:18&lt;/strong&gt; · Here is what I&apos;m deeming acceptable and how I want you to respond.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:24&lt;/strong&gt; · And so when it encounters different types of things in the chat or whatever, it&apos;s going to look at this rules file and try to make a decision based on that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:33&lt;/strong&gt; · MARK THOMPSON: Oh, I love that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:34&lt;/strong&gt; · That&apos;s exactly what we want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:35&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:36&lt;/strong&gt; · The paragraph that we link in the library, this will be used for context for prompting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:47&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:48&lt;/strong&gt; · Let&apos;s see if it does the right thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:50&lt;/strong&gt; · RODY DAVIS: And then also, I would suggest finding some modern snippets of the Angular, TS, CSS, and HTML files that we use-- MARK THOMPSON: Oh, sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:03&lt;/strong&gt; · RODY DAVIS: --even as examples here too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:05&lt;/strong&gt; · MARK THOMPSON: Wow, we can get there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:07&lt;/strong&gt; · OK, so this is pretty close.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:09&lt;/strong&gt; · We&apos;re going to change it a little bit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:10&lt;/strong&gt; · Meet Alex.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:11&lt;/strong&gt; · You don&apos;t have to meet Alex.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:12&lt;/strong&gt; · RODY DAVIS: Yeah, actually, we want to take out the name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:15&lt;/strong&gt; · We&apos;ll do-- yeah, you are a dedicated developer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:20&lt;/strong&gt; · Yeah, we can remove names.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:24&lt;/strong&gt; · Sometimes, that&apos;ll kind of confuse it, but let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:30&lt;/strong&gt; · Is it Angular 18?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:31&lt;/strong&gt; · MARK THOMPSON: No, it&apos;s not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:32&lt;/strong&gt; · But it&apos;s a 18 plus, so I&apos;ll give it a break.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:37&lt;/strong&gt; · I&apos;ll say 20 plus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:39&lt;/strong&gt; · RODY DAVIS: And then, one more Alex.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:40&lt;/strong&gt; · MARK THOMPSON: Ooh, I didn&apos;t even write that, &amp;quot;passionatly adopting signals for reactive state management, embracing standalone--&amp;quot; RODY DAVIS: Right on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:45&lt;/strong&gt; · Right on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:46&lt;/strong&gt; · MARK THOMPSON: Hey, listen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:47&lt;/strong&gt; · Oh!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:47&lt;/strong&gt; · &amp;quot;New control flow for more intuitive template logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:49&lt;/strong&gt; · Performance is paramount to-- paramount to you who constantly seeks to optimize change detection and improve-- wow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:56&lt;/strong&gt; · Uh, excuse me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:57&lt;/strong&gt; · This sounds like a great person.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:58&lt;/strong&gt; · I want to hire this person.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:00&lt;/strong&gt; · RODY DAVIS: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:01&lt;/strong&gt; · OK, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:01&lt;/strong&gt; · So let&apos;s now add a new block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:04&lt;/strong&gt; · So this is a great persona.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:05&lt;/strong&gt; · MARK THOMPSON: Yeah, it&apos;s excellent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:07&lt;/strong&gt; · RODY DAVIS: Now, we&apos;re going to do another top level heading for H2 actually, for examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:15&lt;/strong&gt; · And so we&apos;ll say-- you can do a paragraph right here, and be like, these are modern examples of how to write an Angular 20 component with signals and all the different things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:31&lt;/strong&gt; · MARK THOMPSON: Mmm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:32&lt;/strong&gt; · RODY DAVIS: And then, I would literally paste code blocks with the TS, CSS, and HTML tags.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:38&lt;/strong&gt; · And then, just literally paste something from the docs, because this will help-- or I would do the triple backslash with TS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:47&lt;/strong&gt; · MARK THOMPSON: Triple backslash, like, 1, 2, 3.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:50&lt;/strong&gt; · Like that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:51&lt;/strong&gt; · RODY DAVIS: Uh, backtick.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:51&lt;/strong&gt; · MARK THOMPSON: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:52&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:53&lt;/strong&gt; · Oh, like this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:54&lt;/strong&gt; · RODY DAVIS: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:55&lt;/strong&gt; · Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:55&lt;/strong&gt; · MARK THOMPSON: OK, then we&apos;ll do one for CSS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:58&lt;/strong&gt; · RODY DAVIS: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:59&lt;/strong&gt; · And then, after we do that, we can even say when you update a component, make sure to put the styling in the CSS, the template in HTML, and the logic in TS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:14&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:15&lt;/strong&gt; · RODY DAVIS: Because sometimes, I think you can do a single file Angular [? singular ?] component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:19&lt;/strong&gt; · And I think this will just like, if that&apos;s your preference, you can put that here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:22&lt;/strong&gt; · MARK THOMPSON: Yeah, I&apos;ll do separate because I want to fine tune.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:26&lt;/strong&gt; · I want to improve over time, versus try to nail it on the first-- I guess I&apos;m trying to say we don&apos;t have to zero shot these customizations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:34&lt;/strong&gt; · We can refine them and make them way better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:34&lt;/strong&gt; · RODY DAVIS: Exactly, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:36&lt;/strong&gt; · Yup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:36&lt;/strong&gt; · MARK THOMPSON: When you update a component, be sure to put the logic in the TS file, the CSS-- or the styles --in the CSS file and the HTML template in the HTML file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:56&lt;/strong&gt; · Can I say here is the-- or can I say style guide?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:02&lt;/strong&gt; · Can I do that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:02&lt;/strong&gt; · RODY DAVIS: Yes, of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:03&lt;/strong&gt; · MARK THOMPSON: And then, let me see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:05&lt;/strong&gt; · I have a link to the style guide I&apos;m going to throw in here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:09&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:10&lt;/strong&gt; · So here is a link.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:13&lt;/strong&gt; · RODY DAVIS: You could even throw in how to use signals, and maybe have your signals documentation too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:18&lt;/strong&gt; · MARK THOMPSON: What?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:18&lt;/strong&gt; · Stop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:19&lt;/strong&gt; · RODY DAVIS: Oh yeah, totally Yeah, because assuming it&apos;s using the URL context correctly and our docs are up to date, it&apos;s supposed to parse the URLs as context and ground it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:32&lt;/strong&gt; · MARK THOMPSON: Will it do it every time then, or do I only do it once?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:35&lt;/strong&gt; · And what&apos;s the impact of it if it has to go out and like-- you know what I&apos;m saying?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:39&lt;/strong&gt; · RODY DAVIS: It should cache it, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:41&lt;/strong&gt; · I don&apos;t think it does it every time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:43&lt;/strong&gt; · MARK THOMPSON: Sure, sure, sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:45&lt;/strong&gt; · So here is a link to the style guide, to the most recent Angular style guide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:58&lt;/strong&gt; · So then, I imagine if you are a developer looking to do something like this, so let&apos;s say you&apos;re going to use Firebase Studio at your organization, you might put your appli-- your company style guide doesn&apos;t exist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:13&lt;/strong&gt; · RODY DAVIS: Yeah, totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:14&lt;/strong&gt; · MARK THOMPSON: Maybe that&apos;s a project you do where you define the style guide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:17&lt;/strong&gt; · So that way, your generated code can match.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:20&lt;/strong&gt; · Wow, that&apos;s interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:21&lt;/strong&gt; · RODY DAVIS: So a tip also for people doing that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:24&lt;/strong&gt; · Even if you do have some elaborate resource somewhere, it&apos;s really important to include an NVP minimal representation of that resource in this top level file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:34&lt;/strong&gt; · Because you want the TLDR, and then a way to explore more elaborate information on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:43&lt;/strong&gt; · That also just helps the model to not get overfitted or too confused on one direction, and usually, it&apos;s led to a lot better generation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:55&lt;/strong&gt; · MARK THOMPSON: OK, so I got a little problem here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:58&lt;/strong&gt; · Let me see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:58&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:00&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:00&lt;/strong&gt; · And this is not a problem, but I don&apos;t know how far the context URL reading will go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:08&lt;/strong&gt; · I don&apos;t know how deep It&apos;ll crawl.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:10&lt;/strong&gt; · So what I&apos;ll do is this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:12&lt;/strong&gt; · I have some essentials guides that are the high level, get started with these technologies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:21&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:21&lt;/strong&gt; · MARK THOMPSON: And I wonder if I can throw those in here, and work with these people.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:26&lt;/strong&gt; · RODY DAVIS: That&apos;s exactly how I do my Notebook LLM too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:29&lt;/strong&gt; · I do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:29&lt;/strong&gt; · MARK THOMPSON: Is that right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:30&lt;/strong&gt; · RODY DAVIS: Yeah, mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:31&lt;/strong&gt; · MARK THOMPSON: Look at you, and you use Notebook LLM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:33&lt;/strong&gt; · You&apos;re from the future, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:35&lt;/strong&gt; · RODY DAVIS: [LAUGHS] MARK THOMPSON: Yeah, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:37&lt;/strong&gt; · Let me get back to my-- OK, here we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:39&lt;/strong&gt; · I got too many windows up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:40&lt;/strong&gt; · I&apos;m trying to close them all because we believe, and this is a good test, we believe that if you follow our essentials guides, you will know what you need to know to get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:50&lt;/strong&gt; · RODY DAVIS: OK, awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:52&lt;/strong&gt; · MARK THOMPSON: So I&apos;m guessing we&apos;ll find out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:54&lt;/strong&gt; · If that makes sense, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:56&lt;/strong&gt; · If it does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:57&lt;/strong&gt; · OK, cool, cool, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:59&lt;/strong&gt; · RODY DAVIS: So one thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:00&lt;/strong&gt; · Yeah, let&apos;s also include the examples too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:02&lt;/strong&gt; · MARK THOMPSON: Oh, sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:03&lt;/strong&gt; · RODY DAVIS: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:05&lt;/strong&gt; · MARK THOMPSON: Can I preface this with anything?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:07&lt;/strong&gt; · Can I just say these are-- RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:10&lt;/strong&gt; · I would usually include description, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:13&lt;/strong&gt; · MARK THOMPSON: Here are some links to the essentials for building Angular applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:23&lt;/strong&gt; · Use these to get an understanding of how some of the core functionality works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:42&lt;/strong&gt; · OK, so I think we got something good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:45&lt;/strong&gt; · Let&apos;s pause for a second and answer some questions before we do the examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:49&lt;/strong&gt; · RODY DAVIS: Sweet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:50&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:54&lt;/strong&gt; · OK, right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:55&lt;/strong&gt; · So &amp;quot;Firebase Studio, AI understand Angular better?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:58&lt;/strong&gt; · That&apos;s kind of what we&apos;re working on because as we saw, it does generate working Angular code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:03&lt;/strong&gt; · But we know that you don&apos;t want to generate code and have to spend the whole time refactoring what&apos;s generated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:08&lt;/strong&gt; · The closest we can get to the fidelity that we&apos;re looking for is kind of our aim.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:12&lt;/strong&gt; · RODY DAVIS: And also, everybody has their own preferences too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:14&lt;/strong&gt; · MARK THOMPSON: That&apos;s true.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:15&lt;/strong&gt; · So this file could help you to also define your own preferences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:18&lt;/strong&gt; · Really cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:19&lt;/strong&gt; · &amp;quot;I find AI too silly yet to understand the Angular concepts as simple as human beings can.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:23&lt;/strong&gt; · Well, we&apos;re special, KK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:24&lt;/strong&gt; · You gotta remember that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:25&lt;/strong&gt; · This thing up here, I&apos;m being serious, this thing is special, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:30&lt;/strong&gt; · Your brain is an incredible part of us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:34&lt;/strong&gt; · And artificial intelligence is one thing, but you got to remember something that&apos;s even more important.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:38&lt;/strong&gt; · We&apos;re always working on artificial intelligence, but you&apos;re already intelligent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:42&lt;/strong&gt; · Don&apos;t forget that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:43&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:43&lt;/strong&gt; · I also found I feel like at DevRel, it&apos;s been interesting working both in an age of targeting people and now, LLMs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:55&lt;/strong&gt; · Because now, we start to think about resources that can be consumed easier for beginners, but also find ways to have ramps to more intermediate and advanced resources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:06&lt;/strong&gt; · And the easier that you can clearly explain what you&apos;re trying to do, also helps humans read stuff too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:16&lt;/strong&gt; · So I feel like it&apos;s been cool to see the movements around people add LLMs text files or markdown links to resources on pages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:26&lt;/strong&gt; · Because humans-- markdown is a human readable format, but it&apos;s great for LLMs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:32&lt;/strong&gt; · But by doing that, we&apos;re trying to think about what is the condensed version of our docs that could fit in a markdown file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:39&lt;/strong&gt; · How do we strip out the noise?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:40&lt;/strong&gt; · How do we get it there to make it super easy?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:44&lt;/strong&gt; · And that actually just happens to benefit regular people too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:48&lt;/strong&gt; · MARK THOMPSON: Wow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:49&lt;/strong&gt; · &amp;quot;I dislike that every project that I start in Firebase Studio wants to use React and Next JS, instead of Angular and Firebase Cloud Functions.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:56&lt;/strong&gt; · RODY DAVIS: So, the prototyper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:57&lt;/strong&gt; · Yeah, currently our prototyper uses Next JS and Tailwind and Genkit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:03&lt;/strong&gt; · We wanted to have an opinionated experience that made it really easy to build quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:08&lt;/strong&gt; · We are exploring trying to make that work for more things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:12&lt;/strong&gt; · But right now, it&apos;s not a, I want to generate this thing and it just generates it with whatever framework of your choice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:19&lt;/strong&gt; · Right now, it&apos;s very guided.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:20&lt;/strong&gt; · But I would suggest, especially after we have these AI rules, that you try out Gemini in Firebase, because you can give all the custom rules that you want and how you want it to generate, and still use the super fast previewing, the updating multiple files at once, adding changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:39&lt;/strong&gt; · This still is going to work pretty good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:41&lt;/strong&gt; · One of my favorite-- yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:43&lt;/strong&gt; · MARK THOMPSON: No, no, no, no, you&apos;re fine, you&apos;re fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:45&lt;/strong&gt; · Rody, let me look at the time here, because I know we&apos;ve been hanging out and doing this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:49&lt;/strong&gt; · I know that I only booked you for a certain amount of time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:52&lt;/strong&gt; · RODY DAVIS: No, I&apos;ll be good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:53&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:53&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:54&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:55&lt;/strong&gt; · Yeah, you just flag me if we get out of time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:56&lt;/strong&gt; · OK, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:56&lt;/strong&gt; · &amp;quot;Absolutely that&apos;s usually better, use the AI to tame the AI.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:59&lt;/strong&gt; · Listen, we&apos;re all learning about how to do this stuff together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:02&lt;/strong&gt; · I know it seems like if you look at the news or the internet, it seems like there are an elite set of experts who know how to do everything and that represents everybody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:13&lt;/strong&gt; · That&apos;s not true.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:13&lt;/strong&gt; · We&apos;re all learning how to do this stuff together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:15&lt;/strong&gt; · OK, that&apos;s enough comments for now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:17&lt;/strong&gt; · I&apos;ll come back and we&apos;ll do more content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:19&lt;/strong&gt; · But I want to get this example in here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:21&lt;/strong&gt; · So I&apos;m trying to think of the fastest way to get the code example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:23&lt;/strong&gt; · RODY DAVIS: So where would you want people to go to when they go to the docs to find the best looking versions of modern components or HTML and stuff like that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:36&lt;/strong&gt; · MARK THOMPSON: I don&apos;t know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:37&lt;/strong&gt; · I actually don&apos;t have a good answer for that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:39&lt;/strong&gt; · I don&apos;t have a good answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:40&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:40&lt;/strong&gt; · So then, let&apos;s just basically copy the example that we have in the Workspace, and maybe you could just switch it to signals and stuff like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:47&lt;/strong&gt; · MARK THOMPSON: Yeah, I&apos;m looking at the-- we have our tutorial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:53&lt;/strong&gt; · The tutorial is pretty good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:55&lt;/strong&gt; · So I&apos;m trying to see if I could find like-- oh yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:57&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:58&lt;/strong&gt; · Here you go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:59&lt;/strong&gt; · RODY DAVIS: The other thing that I would suggest to you for the examples is also include doc comments above-- when it&apos;s defining an input signal, be like, this is how you can bind to the template directive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:13&lt;/strong&gt; · And then, doing above the component definition or whatever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:17&lt;/strong&gt; · You could even say for the things that aren&apos;t there, standalone is already enabled true by default or something like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:27&lt;/strong&gt; · MARK THOMPSON: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:28&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:28&lt;/strong&gt; · So let me do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:29&lt;/strong&gt; · Let me see if I can-- I&apos;m going to change one of these examples, because this is an example of some of the things we want to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:36&lt;/strong&gt; · So we&apos;ll change this to a signal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:39&lt;/strong&gt; · We&apos;ll say true.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:40&lt;/strong&gt; · RODY DAVIS: And we probably want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:41&lt;/strong&gt; · Template URL, if that&apos;s your preference for this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:44&lt;/strong&gt; · MARK THOMPSON: Well, it&apos;ll be because that&apos;s what we just told it, to do everything separately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:48&lt;/strong&gt; · So we&apos;ll do template URL and we&apos;ll just call it-- [INAUDIBLE] call at the HTML now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:55&lt;/strong&gt; · Friends at home, don&apos;t get mad at us about the style guide change where we change the file names.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:59&lt;/strong&gt; · Don&apos;t get mad at us about this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:01&lt;/strong&gt; · RODY DAVIS: So one thing that I like to do here too, I&apos;m curious if you&apos;ve done this as well, but I like to use a Mustache style syntax here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:09&lt;/strong&gt; · So if you go up to the app ts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:12&lt;/strong&gt; · MARK THOMPSON: Let me see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:13&lt;/strong&gt; · What can I do here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:14&lt;/strong&gt; · I don&apos;t know if that matters or not, but OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:16&lt;/strong&gt; · RODY DAVIS: So you could, basically, where it says lowercase app, you could do double curly braces and say tag instead to hint to the model that this is the variable that you want to have updated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:32&lt;/strong&gt; · MARK THOMPSON: Oh, like this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:34&lt;/strong&gt; · RODY DAVIS: Yeah, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:35&lt;/strong&gt; · And I would say tag name or whatever, and put that also for app html or-- MARK THOMPSON: What?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:40&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:41&lt;/strong&gt; · RODY DAVIS: Rody, you are from a different planet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:42&lt;/strong&gt; · RODY DAVIS: [LAUGHS] Models are really good at recognizing these patterns.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:47&lt;/strong&gt; · And then, I would also, for the app name, I would say class name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:50&lt;/strong&gt; · MARK THOMPSON: Yeah, like class name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:53&lt;/strong&gt; · And I&apos;m even going to do it like-- so you can kind of get an idea.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:56&lt;/strong&gt; · You know what I mean?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:58&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:58&lt;/strong&gt; · MARK THOMPSON: That&apos;s like that casing I want to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:01&lt;/strong&gt; · RODY DAVIS: Yeah, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:01&lt;/strong&gt; · Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:01&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:02&lt;/strong&gt; · Yeah, the scout case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:03&lt;/strong&gt; · And then, you could do, if you want to do path case up there, instead of a space for tag name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:10&lt;/strong&gt; · MARK THOMPSON: Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:12&lt;/strong&gt; · So like this, with-- RODY DAVIS: You could do path, so the hyphen and stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:17&lt;/strong&gt; · MARK THOMPSON: That&apos;s called path case?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:19&lt;/strong&gt; · RODY DAVIS: Yeah, that&apos;s what I believe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:20&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:20&lt;/strong&gt; · MARK THOMPSON: Oh no, it&apos;s fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:22&lt;/strong&gt; · RODY DAVIS: It&apos;s the same basically underneath.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:22&lt;/strong&gt; · Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:23&lt;/strong&gt; · MARK THOMPSON: Oh, it&apos;s fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:23&lt;/strong&gt; · I just want the people at home to tell me if they&apos;ve seen path case before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:27&lt;/strong&gt; · Tell me if you all have known this and I just didn&apos;t know it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:30&lt;/strong&gt; · Let me know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:31&lt;/strong&gt; · You can&apos;t hurt my feelings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:32&lt;/strong&gt; · My feelings are just not at risk here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:34&lt;/strong&gt; · RODY DAVIS: And then, is there anything else you want to show off in the binding directives?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:41&lt;/strong&gt; · Oh, maybe define a function in there as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:46&lt;/strong&gt; · MARK THOMPSON: Sure, what kind of function are you thinking?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:49&lt;/strong&gt; · RODY DAVIS: I don&apos;t know, like an onclick handler or something like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:52&lt;/strong&gt; · MARK THOMPSON: Oh, sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:53&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:53&lt;/strong&gt; · So we could do-- RODY DAVIS: It doesn&apos;t have to match the HTML.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:56&lt;/strong&gt; · It&apos;s more just an example of-- yeah, I mean, you could.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:59&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:00&lt;/strong&gt; · MARK THOMPSON: And so we can actually show a click here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:03&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:04&lt;/strong&gt; · MARK THOMPSON: So this is definitely the canonical example that I would always use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:08&lt;/strong&gt; · But I think for today, this is fine, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:11&lt;/strong&gt; · RODY DAVIS: Maybe actually, a different one could be the input.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:16&lt;/strong&gt; · So it would show an event type passed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:24&lt;/strong&gt; · MARK THOMPSON: You want me to pass in an input here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:26&lt;/strong&gt; · RODY DAVIS: Like an on change listener, if you&apos;re-- MARK THOMPSON: Oh, like an on change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:30&lt;/strong&gt; · You sure?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:31&lt;/strong&gt; · I don&apos;t have a preference.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:32&lt;/strong&gt; · RODY DAVIS: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:34&lt;/strong&gt; · MARK THOMPSON: All right, but what should this be?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:36&lt;/strong&gt; · So this is an input onto-- like, what type of input are you putting here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:39&lt;/strong&gt; · RODY DAVIS: Like text or something like that, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:41&lt;/strong&gt; · MARK THOMPSON: Sure, we can do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:43&lt;/strong&gt; · But then, I would normally do a NG model here though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:46&lt;/strong&gt; · RODY DAVIS: Oh, well that&apos;s good to know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:47&lt;/strong&gt; · Yeah, I didn&apos;t know what the default is here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:49&lt;/strong&gt; · MARK THOMPSON: Yeah, see, I would have-- RODY DAVIS: Usually in web components, I just bind directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:55&lt;/strong&gt; · MARK THOMPSON: Yeah, see, I would do an NG model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:57&lt;/strong&gt; · So instead of that, let me do the button thing for now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:59&lt;/strong&gt; · RODY DAVIS: Let&apos;s do the button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:59&lt;/strong&gt; · Let&apos;s do the button then.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:00&lt;/strong&gt; · Yeah, great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:00&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:01&lt;/strong&gt; · MARK THOMPSON: And then, so here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:02&lt;/strong&gt; · I&apos;m going to take this out of here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:04&lt;/strong&gt; · And I&apos;ll put it just like in a regular place so it doesn&apos;t think to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:07&lt;/strong&gt; · And then, we&apos;ll say-- RODY DAVIS: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:08&lt;/strong&gt; · MARK THOMPSON: --toggle server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:10&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:10&lt;/strong&gt; · RODY DAVIS: Great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:12&lt;/strong&gt; · MARK THOMPSON: Status.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:13&lt;/strong&gt; · And then, inside this function-- because it doesn&apos;t have to be perfect, but this should be protected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:19&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:19&lt;/strong&gt; · MARK THOMPSON: Read only.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:21&lt;/strong&gt; · And then, we can have our server or our function, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:25&lt;/strong&gt; · RODY DAVIS: That allows you not to update the signal from the template, I&apos;m assuming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:28&lt;/strong&gt; · MARK THOMPSON: No, it&apos;s just so that way, from the class protection, like class [INAUDIBLE]?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:33&lt;/strong&gt; · And so then, that way, you&apos;re not sitting here doing weird stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:38&lt;/strong&gt; · RODY DAVIS: Gotcha.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:38&lt;/strong&gt; · MARK THOMPSON: Yeah, yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:40&lt;/strong&gt; · It&apos;s like, you can&apos;t change the reference to this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:42&lt;/strong&gt; · And then, it&apos;s kind of immutable code but not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:45&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:46&lt;/strong&gt; · So this is right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:48&lt;/strong&gt; · Even in my example, it knew what to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:50&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:51&lt;/strong&gt; · And then we could just do a minimal CSS styling, but it doesn&apos;t have to be a lot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:57&lt;/strong&gt; · In the CSS, I prefer nested CSS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:00&lt;/strong&gt; · So maybe you could show an example of app route or whatever you want for your target selector.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:07&lt;/strong&gt; · MARK THOMPSON: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:08&lt;/strong&gt; · RODY DAVIS: Because if you hint at modern CSS, like a where selector or has query or something like that, I think it&apos;ll help with the direction of the model, to know not to use Tailwind.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:19&lt;/strong&gt; · Or if you do want to use Tailwind, to include that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:22&lt;/strong&gt; · MARK THOMPSON: Never.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:22&lt;/strong&gt; · I don&apos;t know how to use it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:24&lt;/strong&gt; · Not because I don&apos;t like it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:25&lt;/strong&gt; · I don&apos;t how to use it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:27&lt;/strong&gt; · RODY DAVIS: Yeah, I currently don&apos;t use-- I use Tailwind and some stuff, but mostly in the prototyper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:31&lt;/strong&gt; · But yeah, I usually prefer just nested CSS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:34&lt;/strong&gt; · MARK THOMPSON: Sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:35&lt;/strong&gt; · So if I do container, and you&apos;re saying like-- RODY DAVIS: One line down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:39&lt;/strong&gt; · MARK THOMPSON: --inside of here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:41&lt;/strong&gt; · RODY DAVIS: I think you&apos;re outside of the block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:43&lt;/strong&gt; · MARK THOMPSON: I am outside of the block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:45&lt;/strong&gt; · You&apos;re right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:45&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:47&lt;/strong&gt; · So we&apos;re going to do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:49&lt;/strong&gt; · There&apos;s a lot of pressure to put the right example, believe it or not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:52&lt;/strong&gt; · I&apos;m feeling really tense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:53&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:54&lt;/strong&gt; · [LAUGHS] Well, I guess one of the cool things about this is you can iterate on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:58&lt;/strong&gt; · You can check it into version control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:59&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:59&lt;/strong&gt; · RODY DAVIS: If you find that you want to tweak it, I&apos;ll show you how to make changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:03&lt;/strong&gt; · And once we have an example, this is another thing that I use a lot in my AI workflow, is I get the first draft out, and then I use Gemini to refine it and make future edits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:14&lt;/strong&gt; · So you may take this and have it generate more examples based on the ones that you provided.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:19&lt;/strong&gt; · MARK THOMPSON: Oh yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:20&lt;/strong&gt; · RODY DAVIS: And it&apos;s going to be a lot closer to what you&apos;re wanting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:22&lt;/strong&gt; · MARK THOMPSON: That sounds right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:23&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:24&lt;/strong&gt; · And then, I&apos;m going to just say for my container style, and then I&apos;ll say, sure, all that&apos;s fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:29&lt;/strong&gt; · I don&apos;t really care about that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:30&lt;/strong&gt; · RODY DAVIS: Perfect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:31&lt;/strong&gt; · MARK THOMPSON: Yeah, I just want to have something in there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:32&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:32&lt;/strong&gt; · We got a question though, that I think we should answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:35&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:36&lt;/strong&gt; · &amp;quot;Since this is a web app, is it possible to do inference using a locally running LLM?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:41&lt;/strong&gt; · Off a local port.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:42&lt;/strong&gt; · Doing that with VS Code Continue plugin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:46&lt;/strong&gt; · I guess the actual code is remotely hosted.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:48&lt;/strong&gt; · Is there a way to do this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:49&lt;/strong&gt; · RODY DAVIS: I mean, we don&apos;t have a GPU in the VM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:51&lt;/strong&gt; · So if you ran maybe the one billion model from Gemma, you probably could.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:57&lt;/strong&gt; · But yeah, we have a way to do public ports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:00&lt;/strong&gt; · But because Firebase Studio runs in the browser, you would have to have that LLM running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:07&lt;/strong&gt; · But one of the cool things about the Gemini API is it includes Gemma on the URL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:12&lt;/strong&gt; · So if you did want to try out other models, you could even specify that in the chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:16&lt;/strong&gt; · But yeah, using Continue, I don&apos;t think you&apos;d be able to use a powerful enough LLM to make it work locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:26&lt;/strong&gt; · MARK THOMPSON: Wait, stop for a second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:28&lt;/strong&gt; · First off, friends at home, specifically you, Alcatraz.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:30&lt;/strong&gt; · I don&apos;t even know what you&apos;re talking about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:32&lt;/strong&gt; · What is Continue?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:34&lt;/strong&gt; · You all are from a different universe with this cool stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:37&lt;/strong&gt; · I don&apos;t even know what that is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:38&lt;/strong&gt; · Now I have some more stuff I need to look up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:40&lt;/strong&gt; · Let me see if we have a-- &amp;quot;kebab case.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:43&lt;/strong&gt; · OK, you got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:43&lt;/strong&gt; · RODY DAVIS: That&apos;s a good one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:45&lt;/strong&gt; · Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:45&lt;/strong&gt; · MARK THOMPSON: A kebab case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:46&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:46&lt;/strong&gt; · &amp;quot;May you also use pug instead of HTML?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:51&lt;/strong&gt; · RODY DAVIS: I&apos;m not sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:52&lt;/strong&gt; · Is that a common thing with Angular?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:53&lt;/strong&gt; · MARK THOMPSON: Well, no.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:54&lt;/strong&gt; · It&apos;s more because we&apos;re using the Mustache style.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:58&lt;/strong&gt; · RODY DAVIS: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:58&lt;/strong&gt; · MARK THOMPSON: But this is just to help the model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:01&lt;/strong&gt; · This isn&apos;t really like, syntax.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:03&lt;/strong&gt; · RODY DAVIS: Yeah, it&apos;s not explicit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:04&lt;/strong&gt; · It&apos;s kind of pseudocode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:05&lt;/strong&gt; · MARK THOMPSON: Yeah, it&apos;s like pseudocode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:07&lt;/strong&gt; · Yeah, yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:09&lt;/strong&gt; · OK, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:09&lt;/strong&gt; · Make sure we&apos;re answering questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:11&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:11&lt;/strong&gt; · &amp;quot;I got an error in Firebase Studio generate HTML inside the TS, also add component HTML.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:17&lt;/strong&gt; · Interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:17&lt;/strong&gt; · OK, well, all right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:18&lt;/strong&gt; · Enough of my talking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:20&lt;/strong&gt; · Let&apos;s do this and actually try it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:24&lt;/strong&gt; · RODY DAVIS: Let&apos;s add one more line in the CSS to show the nested button selector.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:29&lt;/strong&gt; · MARK THOMPSON: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:30&lt;/strong&gt; · You got to tell me what you mean when you say that, because I&apos;m not sure that I&apos;m familiar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:33&lt;/strong&gt; · RODY DAVIS: Just scroll down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:34&lt;/strong&gt; · Yeah, so add a new line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:35&lt;/strong&gt; · MARK THOMPSON: Oh, inside of here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:37&lt;/strong&gt; · Don&apos;t I need SaaS for that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:39&lt;/strong&gt; · RODY DAVIS: You just do button, and then the open and curly braces.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:44&lt;/strong&gt; · MARK THOMPSON: Yeah, I understand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:45&lt;/strong&gt; · But is this a SaaS thing, or this is a native CSS thing?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:47&lt;/strong&gt; · RODY DAVIS: No, this is native CSS now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:48&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:48&lt;/strong&gt; · MARK THOMPSON: Oh, you can do this now?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:50&lt;/strong&gt; · RODY DAVIS: Yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:51&lt;/strong&gt; · That&apos;s why I was wanting to show it, because it kind of hints that this is a modern approach that&apos;s supported on all the browsers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:56&lt;/strong&gt; · I use this to-- MARK THOMPSON: Stop it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:58&lt;/strong&gt; · RODY DAVIS: Yeah, it&apos;s pretty great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:59&lt;/strong&gt; · And then, we&apos;ll delete that closing brace right there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:02&lt;/strong&gt; · Yeah, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:03&lt;/strong&gt; · MARK THOMPSON: Stop it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:04&lt;/strong&gt; · RODY DAVIS: So this should be great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:05&lt;/strong&gt; · So now, the next thing that we&apos;re going to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:07&lt;/strong&gt; · We have a first draft of our AI rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:10&lt;/strong&gt; · So we&apos;re going to hit Command P, and we&apos;re going to do the triangle, or the forward carat, and we&apos;re going to do rebuild Workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:22&lt;/strong&gt; · MARK THOMPSON: Oh, interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:24&lt;/strong&gt; · So once we-- oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:26&lt;/strong&gt; · So airules.md is loaded at build time, like when the Workspace is being built.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:31&lt;/strong&gt; · RODY DAVIS: You can also load it in the chat by telling Gemini to load the AI rules md file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:37&lt;/strong&gt; · But yeah, by default, it&apos;ll run at Workspace creation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:41&lt;/strong&gt; · Because ideally, you&apos;re not changing the AI rules that much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:45&lt;/strong&gt; · MARK THOMPSON: Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:46&lt;/strong&gt; · RODY DAVIS: You&apos;re explicit about it because everything else should be grounded in the chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:50&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:51&lt;/strong&gt; · OK, Friends at home who are just joining us, I&apos;m joined by the one, the amazing Rody Davis from the Firebase Studio team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:00&lt;/strong&gt; · And we&apos;re working on the airules.md and some code generation in Firebase Studio to provide a better experience for you as an Angular developer when you&apos;re trying to get Gemini to generate code for you, or whatever model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:14&lt;/strong&gt; · So this AI rules works no matter which model, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:18&lt;/strong&gt; · Or is it only for Gemini?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:19&lt;/strong&gt; · RODY DAVIS: Correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:19&lt;/strong&gt; · No, it&apos;s any model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:20&lt;/strong&gt; · Yeah, it&apos;s just the Gemini chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:22&lt;/strong&gt; · MARK THOMPSON: Any-- look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:24&lt;/strong&gt; · [LAUGHS] Look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:26&lt;/strong&gt; · For instance, that&apos;s pretty amazing, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:28&lt;/strong&gt; · That&apos;s not even like, oh, you got use, like, only for Gemini.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:31&lt;/strong&gt; · This is for any model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:32&lt;/strong&gt; · We&apos;re using Gemini.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:33&lt;/strong&gt; · That&apos;s the default model in here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:35&lt;/strong&gt; · But listen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:36&lt;/strong&gt; · We&apos;re trying to get something cool working, because I typed something before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:40&lt;/strong&gt; · Oh, it&apos;s not there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:41&lt;/strong&gt; · It&apos;s OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:42&lt;/strong&gt; · RODY DAVIS: So if you click the History button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:43&lt;/strong&gt; · MARK THOMPSON: Where is it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:44&lt;/strong&gt; · Let me see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:44&lt;/strong&gt; · I don&apos;t see a History button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:45&lt;/strong&gt; · RODY DAVIS: Up by the Add button, on the top left of the-- MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:48&lt;/strong&gt; · RODY DAVIS: You should be able to see that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:50&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:50&lt;/strong&gt; · MARK THOMPSON: [LAUGHS] Let&apos;s go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:53&lt;/strong&gt; · I just want my prompt back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:54&lt;/strong&gt; · That&apos;s what I want, because I want to run it again fresh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:56&lt;/strong&gt; · RODY DAVIS: So I would copy that, and delete the chat right there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:59&lt;/strong&gt; · MARK THOMPSON: Yup, delete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:01&lt;/strong&gt; · Get out of here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:02&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:03&lt;/strong&gt; · MARK THOMPSON: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:04&lt;/strong&gt; · RODY DAVIS: Because it&apos;s showing the old version, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:05&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:06&lt;/strong&gt; · So let&apos;s go over our prompt one more time, and we&apos;ll say create a todo component, updating both app.html, app.css and app.ts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:14&lt;/strong&gt; · Now, the reason we&apos;re listening to files is that Rody was letting us know that when you&apos;re prompting, especially in this context, we&apos;re trying to generate code, giving more specifics can help to get a more accurate output that you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:25&lt;/strong&gt; · RODY DAVIS: Also, I think some people have experienced, in Gemini with Firebase, that they asked for a change but didn&apos;t update anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:32&lt;/strong&gt; · If you&apos;re explicit about saying I want you to update this file, it&apos;ll very likely make those changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:38&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:38&lt;/strong&gt; · So we&apos;re just trying to influence it to do more of what we want?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:42&lt;/strong&gt; · RODY DAVIS: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:42&lt;/strong&gt; · MARK THOMPSON: OK, good, good, good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:43&lt;/strong&gt; · And then, the component should accept an input with the label of todo, the status of the label of the todo, and the status of the todo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:53&lt;/strong&gt; · The component should also strike through the label if the todo is marked as complete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:57&lt;/strong&gt; · So this is a good start.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:59&lt;/strong&gt; · I&apos;m going to submit it using the built-in Gemini model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:03&lt;/strong&gt; · You see that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:03&lt;/strong&gt; · So friends who are just joining, we got some options here, but we&apos;re going to use the built-in Gemini model here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:09&lt;/strong&gt; · And then, I&apos;m going to click Submit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:11&lt;/strong&gt; · And it&apos;s thinking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:13&lt;/strong&gt; · OK, let&apos;s see, while it&apos;s thinking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:14&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:15&lt;/strong&gt; · &amp;quot;Continue is a VS Code extension to get similar functionality to this, but it won&apos;t hit an API in the cloud.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:22&lt;/strong&gt; · Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:22&lt;/strong&gt; · &amp;quot;Instead hits the LLM.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:24&lt;/strong&gt; · Oh, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:25&lt;/strong&gt; · RODY DAVIS: Right, but you need a GPU to be able to run it, and the VM is running in your browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:30&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:31&lt;/strong&gt; · RODY DAVIS: Yeah, but it&apos;s super cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:32&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:33&lt;/strong&gt; · MARK THOMPSON: That is very cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:34&lt;/strong&gt; · So I have to get a computer with a GPU because, I mean, I know my Mac has one, but you know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:41&lt;/strong&gt; · &amp;quot;I really like it.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:42&lt;/strong&gt; · So glad that you are enjoying this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:44&lt;/strong&gt; · &amp;quot;Which is why I would like FB Studio to have local PC remote.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:47&lt;/strong&gt; · Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:47&lt;/strong&gt; · That&apos;s why you would like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:49&lt;/strong&gt; · Got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:49&lt;/strong&gt; · Got it, got it, got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:50&lt;/strong&gt; · &amp;quot;Love the UX for Firebase Studios, so very interested!&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:53&lt;/strong&gt; · Good, awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:54&lt;/strong&gt; · I&apos;m so glad that you all are having a good time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:56&lt;/strong&gt; · Let&apos;s go!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:59&lt;/strong&gt; · OK, I&apos;m glad you&apos;re excited because I&apos;m excited.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:01&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:02&lt;/strong&gt; · &amp;quot;Kevin reposted this stream.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:04&lt;/strong&gt; · Oh, thanks, Kevin, for posting the stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:07&lt;/strong&gt; · Let&apos;s go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:07&lt;/strong&gt; · RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:08&lt;/strong&gt; · MARK THOMPSON: All right, let&apos;s see what happened.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:10&lt;/strong&gt; · RODY DAVIS: OK, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:10&lt;/strong&gt; · Let&apos;s do Review Changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:12&lt;/strong&gt; · Let&apos;s see if it&apos;s picked up our stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:16&lt;/strong&gt; · MARK THOMPSON: So I didn&apos;t tell it anything about-- it didn&apos;t do any of the stuff that I asked it to do, in this case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:22&lt;/strong&gt; · RODY DAVIS: Yeah, let&apos;s make sure that we load it in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:26&lt;/strong&gt; · MARK THOMPSON: Yeah, I never said standalone true as the default. I didn&apos;t say anything about that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:32&lt;/strong&gt; · I mean, I&apos;m sure this code works, but it&apos;s still using the old input and the old out-- RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:38&lt;/strong&gt; · So now, we can do our next level of iteration on the AI rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:43&lt;/strong&gt; · You can explicitly say, what not to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:47&lt;/strong&gt; · MARK THOMPSON: Let&apos;s do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:48&lt;/strong&gt; · Let&apos;s do it, because I want it to be right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:50&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:52&lt;/strong&gt; · So I would do under the HTML block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:57&lt;/strong&gt; · MARK THOMPSON: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:58&lt;/strong&gt; · RODY DAVIS: And you can also close the Gemini chat if you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:01&lt;/strong&gt; · MARK THOMPSON: Yeah, we got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:04&lt;/strong&gt; · We&apos;ll close the preview for right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:07&lt;/strong&gt; · OK, so let&apos;s get to do some more stuff that we want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:09&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:10&lt;/strong&gt; · So let&apos;s do, under the TS file maybe, let&apos;s say, we&apos;ll do the negation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:17&lt;/strong&gt; · We&apos;ll do do not include standalone true because it&apos;s by default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:27&lt;/strong&gt; · MARK THOMPSON: Ugh, typing while people are watching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:30&lt;/strong&gt; · Standalone true because it is default in Angular, I think it&apos;s 19.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:42&lt;/strong&gt; · Did we make standalone true default to 19, chat?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:46&lt;/strong&gt; · Anybody know?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:46&lt;/strong&gt; · RODY DAVIS: It might have been more recent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:48&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:48&lt;/strong&gt; · MARK THOMPSON: Yeah, but let&apos;s just say 20 for that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:50&lt;/strong&gt; · Because this one is for me, so we&apos;ll [? rewrite ?] it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:53&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:55&lt;/strong&gt; · And then, we will also say like-- you could even include-- no.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:04&lt;/strong&gt; · Yeah, that should be fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:05&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:05&lt;/strong&gt; · And then, we&apos;ll want to say, I guess you don&apos;t want to use the input and outputs-- MARK THOMPSON: Right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:12&lt;/strong&gt; · RODY DAVIS: --because that&apos;s the decorators.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:14&lt;/strong&gt; · And so I would do a new line for this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:16&lt;/strong&gt; · MARK THOMPSON: Got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:17&lt;/strong&gt; · Oh, interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:18&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:18&lt;/strong&gt; · Good, good, good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:19&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:28&lt;/strong&gt; · MARK THOMPSON: Use the input signal instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:34&lt;/strong&gt; · Can I say something like, learn more, and then give it a URL?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:38&lt;/strong&gt; · RODY DAVIS: Yeah, I would.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:39&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:39&lt;/strong&gt; · Oh, look.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:40&lt;/strong&gt; · Look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:43&lt;/strong&gt; · [INAUDIBLE] MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:43&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:44&lt;/strong&gt; · MARK THOMPSON: That&apos;s what I want you to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:45&lt;/strong&gt; · RODY DAVIS: And then, I&apos;ll do a period at the end of that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:47&lt;/strong&gt; · And then, I would also add a new space between those two rules, just to give it some separation for tokenization.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:55&lt;/strong&gt; · And then, I would also be explicit about the output one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:59&lt;/strong&gt; · You can also just copy and paste that line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:01&lt;/strong&gt; · MARK THOMPSON: Oh, sure thing, sure thing, sure thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:03&lt;/strong&gt; · RODY DAVIS: I mean, is it an output signal?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:05&lt;/strong&gt; · MARK THOMPSON: But it&apos;s not a signal, but we will-- I would say the output is, it&apos;s syntactically similar, but not a signal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:15&lt;/strong&gt; · RODY DAVIS: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:16&lt;/strong&gt; · Is it a computed?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:17&lt;/strong&gt; · MARK THOMPSON: Nope, it&apos;s just a function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:18&lt;/strong&gt; · RODY DAVIS: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:19&lt;/strong&gt; · Got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:22&lt;/strong&gt; · MARK THOMPSON: Uh, output function instead learn more about the output function here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:34&lt;/strong&gt; · That&apos;s not right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:35&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:35&lt;/strong&gt; · MARK THOMPSON: That&apos;s a good guess, but that&apos;s not right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:37&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:38&lt;/strong&gt; · MARK THOMPSON: That&apos;s so funny.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:39&lt;/strong&gt; · I don&apos;t get offended by when LLMs do stuff like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:42&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:43&lt;/strong&gt; · MARK THOMPSON: But it&apos;s a good guess, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:45&lt;/strong&gt; · And this is also why I tell people all the time, your understanding and your knowledge is super important because if you didn&apos;t know anything and it suggested that, it looks right, it looks correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:02&lt;/strong&gt; · So this is why you&apos;re valuable, because you know what to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:08&lt;/strong&gt; · RODY DAVIS: Totally, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:10&lt;/strong&gt; · MARK THOMPSON: All right, let&apos;s rebuild with just these things and see what happens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:13&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:14&lt;/strong&gt; · So we don&apos;t have to rebuild the workstation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:16&lt;/strong&gt; · I&apos;ll show you the other way how to do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:17&lt;/strong&gt; · MARK THOMPSON: Oh, sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:18&lt;/strong&gt; · RODY DAVIS: So let&apos;s go ahead and close the diff right there, on the third tab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:21&lt;/strong&gt; · MARK THOMPSON: Yeah, and get my Gemini back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:23&lt;/strong&gt; · RODY DAVIS: Let&apos;s do new Gemini chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:24&lt;/strong&gt; · You can actually do the-- in the bottom.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:26&lt;/strong&gt; · Or you can do that too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:27&lt;/strong&gt; · Yeah, that&apos;s perfect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:27&lt;/strong&gt; · MARK THOMPSON: Oh, that&apos;s OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:28&lt;/strong&gt; · I&apos;m just clicking on stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:29&lt;/strong&gt; · RODY DAVIS: Either way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:29&lt;/strong&gt; · Yeah, it&apos;s good to show the different options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:31&lt;/strong&gt; · Let&apos;s copy our prompt and reset again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:35&lt;/strong&gt; · MARK THOMPSON: OK, copy this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:36&lt;/strong&gt; · And then, I&apos;m going to delete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:38&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:38&lt;/strong&gt; · MARK THOMPSON: OK, confirm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:40&lt;/strong&gt; · RODY DAVIS: And then, we&apos;re going to, before we paste in your prompt, say load airules.md.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:45&lt;/strong&gt; · MARK THOMPSON: Load airules.md.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:48&lt;/strong&gt; · Just like that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:48&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:49&lt;/strong&gt; · MARK THOMPSON: Stop it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:50&lt;/strong&gt; · Please, stop it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:51&lt;/strong&gt; · RODY DAVIS: It should read the file for-- yep, perfect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:55&lt;/strong&gt; · MARK THOMPSON: Stop it!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:56&lt;/strong&gt; · RODY DAVIS: Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:57&lt;/strong&gt; · OK, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:57&lt;/strong&gt; · Now, paste in your prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:59&lt;/strong&gt; · MARK THOMPSON: [LAUGHS] What?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:00&lt;/strong&gt; · Stop it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:01&lt;/strong&gt; · That&apos;s so cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:02&lt;/strong&gt; · Oh, man, that&apos;s so cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:03&lt;/strong&gt; · All right, while it&apos;s generating, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:08&lt;/strong&gt; · We saw that comment, make sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:09&lt;/strong&gt; · RODY DAVIS: Looks like it is using the new input and output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:12&lt;/strong&gt; · MARK THOMPSON: Oh, let&apos;s go!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:13&lt;/strong&gt; · It knew what to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:15&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:15&lt;/strong&gt; · MARK THOMPSON: Ha, ha, ha, ha, ha!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:16&lt;/strong&gt; · All right, let me get back to this window here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:18&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:20&lt;/strong&gt; · Ah, that&apos;s amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:22&lt;/strong&gt; · Let&apos;s review our changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:23&lt;/strong&gt; · And we&apos;re already getting closer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:26&lt;/strong&gt; · RODY DAVIS: And also, it doesn&apos;t do the input standalone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:28&lt;/strong&gt; · It didn&apos;t include it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:29&lt;/strong&gt; · MARK THOMPSON: Standalone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:30&lt;/strong&gt; · And it did required?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:32&lt;/strong&gt; · RODY DAVIS: Yeah, that&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:33&lt;/strong&gt; · MARK THOMPSON: [CLAPPING] RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:36&lt;/strong&gt; · MARK THOMPSON: Let&apos;s create this file, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:38&lt;/strong&gt; · RODY DAVIS: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:39&lt;/strong&gt; · MARK THOMPSON: Let&apos;s create that file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:40&lt;/strong&gt; · RODY DAVIS: And you can even do command Enter if you want to do it automatically, but yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:43&lt;/strong&gt; · MARK THOMPSON: I&apos;ll command Enter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:45&lt;/strong&gt; · So anywhere?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:46&lt;/strong&gt; · Just do it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:47&lt;/strong&gt; · RODY DAVIS: Yeah, you can just click Create File or hit Save, either one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:50&lt;/strong&gt; · MARK THOMPSON: Well, I did command Enter from the window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:53&lt;/strong&gt; · RODY DAVIS: Perfect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:54&lt;/strong&gt; · Then now, it&apos;s adding another file because this is going to do a multi-step change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:58&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:58&lt;/strong&gt; · So I&apos;m just going to let it create the file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:01&lt;/strong&gt; · OK, create the file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:02&lt;/strong&gt; · RODY DAVIS: So this is the template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:04&lt;/strong&gt; · And now, it&apos;s going to add the CSS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:05&lt;/strong&gt; · MARK THOMPSON: All right, let me be honest with you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:07&lt;/strong&gt; · Let me be honest with you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:08&lt;/strong&gt; · What you said is starting to become true to me in a new way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:16&lt;/strong&gt; · OK, what happened?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:17&lt;/strong&gt; · Why are you complaining?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:18&lt;/strong&gt; · RODY DAVIS: Now, you should try again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:18&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:19&lt;/strong&gt; · MARK THOMPSON: What?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:20&lt;/strong&gt; · It didn&apos;t like something it did?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:21&lt;/strong&gt; · RODY DAVIS: No, I think it hit an error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:23&lt;/strong&gt; · It&apos;ll be an agentive loop, and it&apos;ll go past it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:25&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:30&lt;/strong&gt; · MARK THOMPSON: So what I was going to say is that, so while we&apos;re still the whole prototyping thing, while that&apos;s happening, I think what&apos;s interesting is that with the sophisticated enough prompt, and a sophisticated enough AI rules, I can kind of simulate a prototyper right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:50&lt;/strong&gt; · RODY DAVIS: For sure, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:52&lt;/strong&gt; · Also, if you go to line 3, I think it updated a full path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:56&lt;/strong&gt; · Let&apos;s make that a relative path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:57&lt;/strong&gt; · MARK THOMPSON: Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:59&lt;/strong&gt; · RODY DAVIS: Yeah, I don&apos;t know why it pulled from this report.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:03&lt;/strong&gt; · We can just make it a todo.ts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:05&lt;/strong&gt; · MARK THOMPSON: Oh yeah. yeah, yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:07&lt;/strong&gt; · RODY DAVIS: Probably a sample file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:11&lt;/strong&gt; · MARK THOMPSON: OK, and is it ./todo?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:13&lt;/strong&gt; · Where is it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:13&lt;/strong&gt; · Where is it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:14&lt;/strong&gt; · RODY DAVIS: Todo.js, it looks like.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:16&lt;/strong&gt; · MARK THOMPSON: Oh, that can&apos;t be right then.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:18&lt;/strong&gt; · We gotta fix that, which we can.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:19&lt;/strong&gt; · We can fix that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:20&lt;/strong&gt; · RODY DAVIS: Yup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:20&lt;/strong&gt; · MARK THOMPSON: There is no todo.js.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:22&lt;/strong&gt; · RODY DAVIS: Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:23&lt;/strong&gt; · Why?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:23&lt;/strong&gt; · Where is this file then?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:26&lt;/strong&gt; · MARK THOMPSON: OK, so let&apos;s fix that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:28&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:29&lt;/strong&gt; · MARK THOMPSON: We can say-- because I don&apos;t know where-- what&apos;s going on So we should be able to just do dot slash, and then todo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:39&lt;/strong&gt; · Yeah, there you go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:41&lt;/strong&gt; · Not component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:41&lt;/strong&gt; · Yeah, not component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:42&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:42&lt;/strong&gt; · We just didn&apos;t need to include the suffix.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:44&lt;/strong&gt; · And then, router outlet-- MARK THOMPSON: It&apos;s just not being used.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:47&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:47&lt;/strong&gt; · MARK THOMPSON: This is a Angular language service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:49&lt;/strong&gt; · RODY DAVIS: OK, cool, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:50&lt;/strong&gt; · MARK THOMPSON: So we got that, and I can&apos;t wait till we get selected lists in general, so you don&apos;t have to do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:55&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:56&lt;/strong&gt; · MARK THOMPSON: That&apos;s coming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:57&lt;/strong&gt; · We&apos;re working on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:58&lt;/strong&gt; · RODY DAVIS: So how would that work with-- does it just bind to the root or something?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:05&lt;/strong&gt; · MARK THOMPSON: Yeah, it&apos;s a surprisingly complex problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:08&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:08&lt;/strong&gt; · MARK THOMPSON: That&apos;s why we haven&apos;t released it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:09&lt;/strong&gt; · [LAUGH] RODY DAVIS: OK, got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:10&lt;/strong&gt; · [LAUGHS] MARK THOMPSON: It&apos;s surprisingly complex-- RODY DAVIS: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:14&lt;/strong&gt; · MARK THOMPSON: --to do this, but we are working on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:16&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:17&lt;/strong&gt; · Where&apos;s my preview?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:17&lt;/strong&gt; · RODY DAVIS: OK, so now, if you ever want to pull up your preview, I think we actually added it to the toolbar on the bottom, but you can also do it from the command block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:23&lt;/strong&gt; · MARK THOMPSON: Oh yeah, I see it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:24&lt;/strong&gt; · I see it right there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:25&lt;/strong&gt; · Preview, right next to Gemini.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:27&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:29&lt;/strong&gt; · Perfect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:30&lt;/strong&gt; · MARK THOMPSON: OK, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:31&lt;/strong&gt; · RODY DAVIS: So this is a cool-- I think we can actually show off-- is this the warning or is this an error?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:37&lt;/strong&gt; · MARK THOMPSON: That&apos;s the error, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:38&lt;/strong&gt; · The type of-- RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:40&lt;/strong&gt; · So we can actually copy this error into Gemini and have it help us fix it if you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:43&lt;/strong&gt; · MARK THOMPSON: I&apos;m going to copy everything, even the line number.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:46&lt;/strong&gt; · RODY DAVIS: If you scroll down, I think there is a-- oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:50&lt;/strong&gt; · If you click up on the web console, the error on the bottom-- MARK THOMPSON: Oh, up?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:54&lt;/strong&gt; · Right here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:54&lt;/strong&gt; · Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:55&lt;/strong&gt; · Yes, yes, yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:56&lt;/strong&gt; · RODY DAVIS: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:57&lt;/strong&gt; · That&apos;s just vite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:57&lt;/strong&gt; · Sometimes the error will show there, and we have a continue in Gemini there as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:02&lt;/strong&gt; · So yeah, we just copy the full thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:04&lt;/strong&gt; · MARK THOMPSON: Yeah, so I&apos;m just going to-- now, we know how to fix this, chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:09&lt;/strong&gt; · RODY DAVIS: What is that error, by the way?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:11&lt;/strong&gt; · MARK THOMPSON: It looks like the type, because-- let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:14&lt;/strong&gt; · OK, this is correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:16&lt;/strong&gt; · RODY DAVIS: Is it use.ts, or-- MARK THOMPSON: Yeah, so I&apos;m looking at this to see what the types are here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:20&lt;/strong&gt; · So the types look correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:21&lt;/strong&gt; · So if we go todo component, maybe something about the input is not right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:26&lt;/strong&gt; · So it has a todo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:28&lt;/strong&gt; · Why don&apos;t you like this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:29&lt;/strong&gt; · You should like this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:30&lt;/strong&gt; · This shouldn&apos;t be a problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:32&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:32&lt;/strong&gt; · Well, maybe we could restart the web preview maybe, just as a hard restart?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:36&lt;/strong&gt; · MARK THOMPSON: I don&apos;t know, let me see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:38&lt;/strong&gt; · Even though-- oh, that&apos;s not that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:40&lt;/strong&gt; · OK, &amp;quot;Here&apos;s another rule: use signalStore for state management.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:44&lt;/strong&gt; · RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:44&lt;/strong&gt; · MARK THOMPSON: What&apos;s up, Alex?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:46&lt;/strong&gt; · What&apos;s up?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:46&lt;/strong&gt; · What&apos;s up?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:47&lt;/strong&gt; · RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:48&lt;/strong&gt; · MARK THOMPSON: Good to see you, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:49&lt;/strong&gt; · Oh, so the people say, yeah, we did announce it in 19.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:53&lt;/strong&gt; · I thought it was 19.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:54&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:54&lt;/strong&gt; · MARK THOMPSON: OK, since angular-- la, la, la, la, signal, changedection onpush still required on components-- OK, I&apos;m going to answer this after, but Rody doesn&apos;t have as much time as I do for this today, so I don&apos;t want to lose his time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:04&lt;/strong&gt; · So I will answer your question after, friends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:06&lt;/strong&gt; · Don&apos;t you worry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:07&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:08&lt;/strong&gt; · MARK THOMPSON: All right, let me put this in the chat, and let&apos;s see if we can get it to just figure out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:13&lt;/strong&gt; · So do I have to type anything, or I can just [INAUDIBLE] it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:15&lt;/strong&gt; · RODY DAVIS: Yeah, you should just do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:16&lt;/strong&gt; · Yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:17&lt;/strong&gt; · MARK THOMPSON: What?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:17&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:18&lt;/strong&gt; · MARK THOMPSON: I usually want to be nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:19&lt;/strong&gt; · I can&apos;t change my-- RODY DAVIS: You can.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:20&lt;/strong&gt; · So sometimes, I put the error inside of a code block and I say fix this error or whatever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:29&lt;/strong&gt; · But I&apos;ve also found that sometimes just pasting it works too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:32&lt;/strong&gt; · MARK THOMPSON: No, I don&apos;t know why I like to do it this way, but-- [LAUGHS] RODY DAVIS: No, it&apos;s a good way to kind of guide it, so you&apos;re into an active instead of a passive chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:45&lt;/strong&gt; · MARK THOMPSON: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:46&lt;/strong&gt; · RODY DAVIS: But I do think that&apos;s a good approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:48&lt;/strong&gt; · MARK THOMPSON: Now, let&apos;s see what it wants to change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:51&lt;/strong&gt; · Why do you want to change so much?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:52&lt;/strong&gt; · RODY DAVIS: So you can hit Review.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:53&lt;/strong&gt; · MARK THOMPSON: Yeah, what do you want to do?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:54&lt;/strong&gt; · What do you want to do?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:55&lt;/strong&gt; · Nope, I don&apos;t want you to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:57&lt;/strong&gt; · No.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:58&lt;/strong&gt; · RODY DAVIS: Yeah, so you can hit the Undo on the left, on the diff, all the way on the left to the green.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:07&lt;/strong&gt; · MARK THOMPSON: Oh, here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:08&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:08&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:09&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:10&lt;/strong&gt; · MARK THOMPSON: Is that the only change you want to do, &apos;cause that ain&apos;t right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:12&lt;/strong&gt; · RODY DAVIS: And then just hit Save, I think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:14&lt;/strong&gt; · MARK THOMPSON: Yeah, but why does it want to do that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:17&lt;/strong&gt; · RODY DAVIS: And then, close out that file too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:19&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:21&lt;/strong&gt; · Uh, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:22&lt;/strong&gt; · RODY DAVIS: This is part of prompting models.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:24&lt;/strong&gt; · If there&apos;s something that&apos;s overfitted into a training set of old versions of CSS or old versions of Angular, we have to teach it how to do the new stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:33&lt;/strong&gt; · MARK THOMPSON: So what does it think is the problem?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:37&lt;/strong&gt; · OK, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:37&lt;/strong&gt; · Type of todo component undefined is not accessible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:40&lt;/strong&gt; · Oh, why would it be the-- no, the type is wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:44&lt;/strong&gt; · RODY DAVIS: Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:46&lt;/strong&gt; · MARK THOMPSON: Why do you think you want to use todo component?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:49&lt;/strong&gt; · Is that what happened in our app.ts?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:52&lt;/strong&gt; · Let&apos;s look.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:53&lt;/strong&gt; · Let&apos;s have a look.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:54&lt;/strong&gt; · RODY DAVIS: Maybe it did an import issue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:56&lt;/strong&gt; · MARK THOMPSON: Yeah, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:57&lt;/strong&gt; · So we got import.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:58&lt;/strong&gt; · So this is correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:59&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:00&lt;/strong&gt; · And if we-- MARK THOMPSON: What?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:03&lt;/strong&gt; · Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:04&lt;/strong&gt; · That&apos;s why.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:06&lt;/strong&gt; · Wait.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:07&lt;/strong&gt; · No, that&apos;s correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:09&lt;/strong&gt; · RODY DAVIS: Do we need the router outlet or anything else here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:12&lt;/strong&gt; · MARK THOMPSON: OK, hold on one second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:14&lt;/strong&gt; · Let me-- we don&apos;t need the router outlet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:16&lt;/strong&gt; · But I want to say-- OK, we can get this out of here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:20&lt;/strong&gt; · Let me make sure, because I think it followed the style guide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:23&lt;/strong&gt; · RODY DAVIS: Mm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:25&lt;/strong&gt; · MARK THOMPSON: And so it has to do .ts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:27&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:27&lt;/strong&gt; · I want to make sure that todo-- so this is todo component, which is what we want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:34&lt;/strong&gt; · So then, let me go back and let me have a look.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:39&lt;/strong&gt; · RODY DAVIS: And also, I want to-- MARK THOMPSON: Oh, now, it&apos;s working.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:43&lt;/strong&gt; · RODY DAVIS: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:43&lt;/strong&gt; · MARK THOMPSON: It was never broken.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:44&lt;/strong&gt; · It just, maybe needs to be restarted, like you said a long time ago.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:46&lt;/strong&gt; · RODY DAVIS: No worries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:47&lt;/strong&gt; · MARK THOMPSON: Oh, look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:49&lt;/strong&gt; · RODY DAVIS: Oh, right on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:50&lt;/strong&gt; · [LAUGHS] Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:52&lt;/strong&gt; · Yeah, maybe it just had an old version of the app component and was hot reloading it, because we do try to do the hot module replacement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:00&lt;/strong&gt; · MARK THOMPSON: Yeah, look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:02&lt;/strong&gt; · We did it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:03&lt;/strong&gt; · RODY DAVIS: Right on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:04&lt;/strong&gt; · MARK THOMPSON: I mean-- RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:05&lt;/strong&gt; · MARK THOMPSON: What were you going to say?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:06&lt;/strong&gt; · I&apos;m sorry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:06&lt;/strong&gt; · RODY DAVIS: I was going to say, so one of the cool things about this is, one, like I said, you can check it into version control and you can include it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:14&lt;/strong&gt; · Also, I would really love to ask from your community too, if there&apos;s an amazing rules file that we can create, let&apos;s put it in the official Firebase Studio template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:24&lt;/strong&gt; · And then, one other thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:26&lt;/strong&gt; · If you click on the Firebase Studio extension on the left sidebar-- MARK THOMPSON: OK, left sidebar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:31&lt;/strong&gt; · This?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:32&lt;/strong&gt; · RODY DAVIS: The bottom icon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:33&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:34&lt;/strong&gt; · RODY DAVIS: One of the cool things that you may run into is like, I&apos;m wanting to work in a Workspace, but it&apos;s not in GitHub yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:42&lt;/strong&gt; · Well, if you click on the Share icon, right there beside-- MARK THOMPSON: Stop it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:47&lt;/strong&gt; · RODY DAVIS: Yeah, you can actually add people&apos;s emails and give them a link to Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:52&lt;/strong&gt; · And you can be working in pair programming in the same machine as if you&apos;re literally right next to each other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:57&lt;/strong&gt; · So it&apos;s really great for synchronous programming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:01&lt;/strong&gt; · And then also, we have plenty of hosting and deployment options there too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:04&lt;/strong&gt; · So if you hit Cancel right here and scroll down as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:08&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:09&lt;/strong&gt; · RODY DAVIS: Even if you want a Gemini API key, we can auto provision one for you here without you having to leave Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:16&lt;/strong&gt; · And there&apos;s a bunch of other interesting stuff as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:19&lt;/strong&gt; · So a lot of cool things to try out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:23&lt;/strong&gt; · Even if you&apos;re used to working locally, you can use this as a sidecar to develop, and sometimes when you want a different experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:31&lt;/strong&gt; · And then, if you&apos;re an Angular library author or someone that&apos;s working in some type of examples and stuff, we make it really easy for you to have an open end Firebase Studio button that you can just add to your markdown docs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:46&lt;/strong&gt; · And so someone can open up and have this experience already without having to do anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:50&lt;/strong&gt; · MARK THOMPSON: We had that for IDX on A-Dev, to open an IDX.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:55&lt;/strong&gt; · So we&apos;re going to update it to be open in Firebase Studio, and I would love to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:00&lt;/strong&gt; · And in terms of that file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:02&lt;/strong&gt; · RODY DAVIS: Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:02&lt;/strong&gt; · MARK THOMPSON: Today&apos;s exercise already was to start that template file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:06&lt;/strong&gt; · RODY DAVIS: Sweet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:07&lt;/strong&gt; · MARK THOMPSON: So we want to totally get community support on this too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:11&lt;/strong&gt; · We&apos;re going to check this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:12&lt;/strong&gt; · So here&apos;s what&apos;s going to happen, friends at home, as you&apos;re watching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:15&lt;/strong&gt; · Like I said, as we&apos;re building, you all may have experiences and things that need to go into these AI rules that is going to benefit everybody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:22&lt;/strong&gt; · So our AI rules is going to end up, not only on GitHub, but we&apos;re also going to put it on A-Dev.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:28&lt;/strong&gt; · And so you can go there and be like, hey, we need to update this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:31&lt;/strong&gt; · We need to add these better things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:32&lt;/strong&gt; · Here&apos;s some better canonical examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:34&lt;/strong&gt; · Let&apos;s work together to make the prompts and the tools that we need to serve the Angular community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:40&lt;/strong&gt; · So we&apos;re really excited about it, because I would love to see the airules.md be able to be as effective even in an enterprise scenario.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:48&lt;/strong&gt; · Because right now, it works in a todo app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:50&lt;/strong&gt; · But our community are enterprise developers, and they&apos;re building, scalable web apps that serve millions of people.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:58&lt;/strong&gt; · And we want to make sure that when they&apos;re doing code generation, especially if they&apos;re going to use a tool like this, they get the best outcome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:03&lt;/strong&gt; · So I&apos;m super excited.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:04&lt;/strong&gt; · Rody, this has been so much fun today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:06&lt;/strong&gt; · RODY DAVIS: Yeah, and then one other thing on AI rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:08&lt;/strong&gt; · MARK THOMPSON: Yeah, please.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:09&lt;/strong&gt; · RODY DAVIS: Also, just for viewers, try not to think about them as a one size fits all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:15&lt;/strong&gt; · These are like the opposite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:17&lt;/strong&gt; · It&apos;s a specific context-driven way to ground the model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:22&lt;/strong&gt; · So if you want to have multiple AI rules at your company, you can have one for your beginners and junior developers that are getting onboarded to the code base, and you can have another one for more advanced developers that know more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:37&lt;/strong&gt; · So you could say for a junior developer, you could have it be like, always explain the changes and include a list of steps on how to do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:47&lt;/strong&gt; · Or like, always use a chain of thought to be able to say what you&apos;re going to do before doing it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:52&lt;/strong&gt; · And you could have a model that&apos;s more elaborate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:56&lt;/strong&gt; · And then, for a senior dev, you can be like, make sure to always update tests and run the tests, and make sure that you&apos;re always doing these three things every time you make a change to a file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:07&lt;/strong&gt; · Or make sure the readme is always up to date or the chain of-- you can customize it, but it&apos;s not a one size fits all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:13&lt;/strong&gt; · You definitely want to have different experiences for different people coming to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:17&lt;/strong&gt; · MARK THOMPSON: Wow, incredible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:19&lt;/strong&gt; · Absolutely incredible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:21&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:22&lt;/strong&gt; · I think, Rody, we got a few minutes left.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:24&lt;/strong&gt; · RODY DAVIS: I am good, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:25&lt;/strong&gt; · MARK THOMPSON: Yeah, let me see if there are any questions or comments that-- RODY DAVIS: Oh, one-- sorry --one more thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:32&lt;/strong&gt; · MARK THOMPSON: Yeah, please.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:33&lt;/strong&gt; · RODY DAVIS: Can you click on the preview, on the top right of the web preview?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:37&lt;/strong&gt; · MARK THOMPSON: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:38&lt;/strong&gt; · RODY DAVIS: We have a couple of different things, which are important to call out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:41&lt;/strong&gt; · One is we have Lighthouse built right in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:44&lt;/strong&gt; · So to the right sidebar, if you click that, you can actually run Lighthouse on your web app directly from Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:52&lt;/strong&gt; · So you can close that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:54&lt;/strong&gt; · The other thing that you can do is if you click on that share icon, the second link right beside that one-- MARK THOMPSON: This one?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:01&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:03&lt;/strong&gt; · If you click on that, we will have a QR code where you could actually run-- so it&apos;s not public right now, so you don&apos;t have to worry about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:10&lt;/strong&gt; · MARK THOMPSON: I was going to say, I&apos;m not showing no one my QR code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:12&lt;/strong&gt; · No way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:13&lt;/strong&gt; · RODY DAVIS: Yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:13&lt;/strong&gt; · So since it&apos;s private, nobody&apos;s going to be able to connect to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:15&lt;/strong&gt; · MARK THOMPSON: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:16&lt;/strong&gt; · RODY DAVIS: So essentially, this will have a QR code where people can scan and run it on their phone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:22&lt;/strong&gt; · MARK THOMPSON: [GASPS] RODY DAVIS: And by making preview public, you could make it available to everyone while your VM is active.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:28&lt;/strong&gt; · But right now, you would only be able to run it because you have access to the VM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:32&lt;/strong&gt; · And then, the Final button is on the icon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:36&lt;/strong&gt; · You can actually open up the web preview into a new tab, and that should open up in this Chrome window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:43&lt;/strong&gt; · And so you can have a full screen version of it, which allows you to use the built-in Chrome DevTools, like you may be used to, or maybe have two tabs open.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:53&lt;/strong&gt; · So there&apos;s a lot of flexibility even in customizing this experience too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:57&lt;/strong&gt; · MARK THOMPSON: Fantastic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:59&lt;/strong&gt; · OK, friends, let&apos;s get some questions-- RODY DAVIS: Sweet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:01&lt;/strong&gt; · MARK THOMPSON: --about Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:03&lt;/strong&gt; · Let&apos;s get some questions about working with LLMs in Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:07&lt;/strong&gt; · Over to Rody right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:08&lt;/strong&gt; · This is a great time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:09&lt;/strong&gt; · We&apos;re going to answer some questions, friends, and we&apos;ll go from there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:13&lt;/strong&gt; · Did I see this comment?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:14&lt;/strong&gt; · &amp;quot;Here&apos;s another rule: use signalStore.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:16&lt;/strong&gt; · RODY DAVIS: Yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:17&lt;/strong&gt; · MARK THOMPSON: I think it&apos;s pretty great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:19&lt;/strong&gt; · &amp;quot;Pasting works - saves time as well.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:21&lt;/strong&gt; · Agree to that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:21&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:22&lt;/strong&gt; · &amp;quot;I was on Alex workshop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:23&lt;/strong&gt; · Nice to see all of you here.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:24&lt;/strong&gt; · Hey, Here, Alex is awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:27&lt;/strong&gt; · I just got to see Alex at Google I/O recently, I believe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:29&lt;/strong&gt; · RODY DAVIS: Oh, awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:30&lt;/strong&gt; · MARK THOMPSON: Were we at Google I/O, Alex, together?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:33&lt;/strong&gt; · Alex, yes, used to work on Firebase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:35&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:36&lt;/strong&gt; · Yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:37&lt;/strong&gt; · &amp;quot;Don&apos;t use text decoration like triple ticks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:40&lt;/strong&gt; · It takes extra tokens.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:42&lt;/strong&gt; · Should we have consideration about this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:44&lt;/strong&gt; · RODY DAVIS: Yeah, I guess this is a preference, but I found that Gemini is usually pretty good at encapsulating where you&apos;re defining code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:52&lt;/strong&gt; · If you don&apos;t want to use triple backticks, some people use XML opening and closing tags.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:57&lt;/strong&gt; · So you could say, example open, example close.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:00&lt;/strong&gt; · MARK THOMPSON: Oh, sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:00&lt;/strong&gt; · I&apos;ve seen this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:01&lt;/strong&gt; · RODY DAVIS: Yeah, but I do like to wrap my code, so it&apos;s very clear that this is semantically different.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:08&lt;/strong&gt; · MARK THOMPSON: So if tokens are important, if you&apos;re trying to conserve tokens, because again, you know-- RODY DAVIS: Gemini does have a million tokens, so you do have plenty of context window here as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:18&lt;/strong&gt; · MARK THOMPSON: Yeah, but it costs you money to use the tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:20&lt;/strong&gt; · RODY DAVIS: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:20&lt;/strong&gt; · For sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:20&lt;/strong&gt; · MARK THOMPSON: So I get it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:21&lt;/strong&gt; · I get it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:21&lt;/strong&gt; · Good feedback.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:22&lt;/strong&gt; · So, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:24&lt;/strong&gt; · &amp;quot;Hey--&amp;quot; who is this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:25&lt;/strong&gt; · Oh, what&apos;s up Hamza?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:26&lt;/strong&gt; · Good to see you, as always.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:28&lt;/strong&gt; · Nice background.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:29&lt;/strong&gt; · Thank you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:30&lt;/strong&gt; · This is real, and I built this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:31&lt;/strong&gt; · RODY DAVIS: [LAUGHS] That&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:32&lt;/strong&gt; · MARK THOMPSON: I always tell people that I built this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:34&lt;/strong&gt; · This is a real background.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:36&lt;/strong&gt; · It&apos;s not a thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:37&lt;/strong&gt; · All right, Lars, &amp;quot;Good stuff.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:38&lt;/strong&gt; · I&apos;m so glad that you&apos;re enjoying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:41&lt;/strong&gt; · Yeah, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:42&lt;/strong&gt; · Very cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:43&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:43&lt;/strong&gt; · I mean, so I think the next step, what I would love to do, like I said, we&apos;re going to develop this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:48&lt;/strong&gt; · Some other people on the engineering team are also working on some prompts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:51&lt;/strong&gt; · I would love to see if I could combine the prompts together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:57&lt;/strong&gt; · And I&apos;m really interested in trying to make a prototype in Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:01&lt;/strong&gt; · RODY DAVIS: Yes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:02&lt;/strong&gt; · MARK THOMPSON: I e-mode just by giving it really good instructions in AI rules, and a really nice prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:10&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:10&lt;/strong&gt; · In fact, something kind of maybe as an experiment for viewers at home, go to Gemini and ask it to do a common Angular CLI command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:22&lt;/strong&gt; · Because one of the cool things about Gemini in Firebase is it can run terminal commands directly from the chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:28&lt;/strong&gt; · MARK THOMPSON: Like tool calling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:29&lt;/strong&gt; · RODY DAVIS: Yeah, exactly like tool calling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:31&lt;/strong&gt; · So you could even say, when you add a new component, run Ng create the component name, to where it even is running the CLI for Angular for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:41&lt;/strong&gt; · And then, you have it update the files after that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:43&lt;/strong&gt; · MARK THOMPSON: Which is what you kind of want in some cases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:45&lt;/strong&gt; · Because here&apos;s the thing that we know about LLMs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:48&lt;/strong&gt; · They&apos;re non-deterministic, so you might get some things that happen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:51&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:52&lt;/strong&gt; · MARK THOMPSON: But if you&apos;re able to leverage the existing deterministic tools in conjunction with some of the behavior that LLMs are really good at.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:01&lt;/strong&gt; · Because I&apos;m looking at LLM-- and I saw somebody say this, and I think it&apos;s really accurate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:06&lt;/strong&gt; · Kelsey Hightower said this once on Bluesky.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:09&lt;/strong&gt; · He was like, LLMs are really great for cases where there would be a lot of if statements in your programming, because you have to make so many decisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:19&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:19&lt;/strong&gt; · MARK THOMPSON: And I&apos;m like, yeah, I want it to be able to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:22&lt;/strong&gt; · But also, I already know how to make a component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:24&lt;/strong&gt; · And I know how to make a component to the current guidelines that I have, to my current project setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:30&lt;/strong&gt; · So like being able to do both together is really interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:33&lt;/strong&gt; · RODY DAVIS: Yeah, totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:34&lt;/strong&gt; · Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:35&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:36&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:36&lt;/strong&gt; · &amp;quot;This is in browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:38&lt;/strong&gt; · Any performance overhead--&amp;quot; oh, good question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:40&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:41&lt;/strong&gt; · &amp;quot;This is in browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:42&lt;/strong&gt; · Any performance overhead with this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:43&lt;/strong&gt; · Could we see a desktop native version?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:46&lt;/strong&gt; · A lot of interest in the Zed editor, which is Rust-based, due VSCodium derivatives, which I assume this is built on.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:54&lt;/strong&gt; · So is there a future or any ideas around a Firebase Studio native version?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:00&lt;/strong&gt; · RODY DAVIS: So one of the interesting things is we currently support web, and I think a lot of people ask for a native desktop version.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:09&lt;/strong&gt; · And I think that a lot of people have really nice powerful dev machines, and I think that&apos;s really cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:17&lt;/strong&gt; · But we see a lot in emerging markets or people in education or people around the world.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:24&lt;/strong&gt; · Having access to just a web browser is already a huge benefit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:28&lt;/strong&gt; · So if you can go to a URL and have a full VM workstation set up every time, that&apos;s a huge deal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:35&lt;/strong&gt; · And the other thing with that too is, I think taking Firebase Studio out of the computer would be even more interesting too, like in XR environments, whether it be on Apple or Android.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:48&lt;/strong&gt; · And so now, you&apos;re in VR coding, which I&apos;ve done a lot of.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:51&lt;/strong&gt; · And you can&apos;t do that with a local development, but you can with a web-based IDE.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:56&lt;/strong&gt; · And so I think trying to get beyond the limitations of just having a really powerful CPU.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:04&lt;/strong&gt; · Now, you can have a shareable workspace that is very collaborative and can have multiple people working in it, is an experience that&apos;s hard to do when everybody has an installed desktop version.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:14&lt;/strong&gt; · And we&apos;re trying to get away from everybody having to set up and configure their path, because everybody has a unique version of dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:23&lt;/strong&gt; · Not only that, we&apos;re built on top of NixOS which allows you to have your environment work every time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:28&lt;/strong&gt; · MARK THOMPSON: So from a-- and actually, we worked together on a project that I was doing where I was like, I have to do a class or something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:38&lt;/strong&gt; · I was teaching some Angular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:39&lt;/strong&gt; · Oh, I was teaching Angular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:40&lt;/strong&gt; · That&apos;s literally it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:41&lt;/strong&gt; · I was teaching Angular and I needed everybody to have no install time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:47&lt;/strong&gt; · And I was like, I don&apos;t want anybody&apos;s machine configuration, even though I did end up running into a problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:52&lt;/strong&gt; · But that was a my problem, where I didn&apos;t include, I think, an NPM install in the Nix file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:59&lt;/strong&gt; · So then when people were trying to get started, they couldn&apos;t get started immediately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:03&lt;/strong&gt; · But the point being though, from an education point of view especially, a pre-configured environment that has the same capabilities regardless of the machine is really interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:13&lt;/strong&gt; · RODY DAVIS: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:13&lt;/strong&gt; · MARK THOMPSON: But this is a good point, because we have a range of developers out there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:17&lt;/strong&gt; · RODY DAVIS: Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:17&lt;/strong&gt; · MARK THOMPSON: Some folks have really powerful machines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:19&lt;/strong&gt; · It sounds like Alcatraz has a really powerful machine, because he&apos;s running Ollama and running VS Continue, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:23&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:24&lt;/strong&gt; · MARK THOMPSON: So he wants to take advantage of the power locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:27&lt;/strong&gt; · So that makes sense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:28&lt;/strong&gt; · RODY DAVIS: Yeah, totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:29&lt;/strong&gt; · MARK THOMPSON: Yeah, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:30&lt;/strong&gt; · &amp;quot;Good job, you learn a lot when our environment is Angular.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:33&lt;/strong&gt; · Yeah, you do learn a lot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:35&lt;/strong&gt; · I love Angular, but I&apos;m biased because I&apos;ve been working on this project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:38&lt;/strong&gt; · Oh, friends, guess what this month is?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:40&lt;/strong&gt; · June is five years since I joined the Angular team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:44&lt;/strong&gt; · RODY DAVIS: Wow, that is awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:46&lt;/strong&gt; · MARK THOMPSON: Yeah, because you and I joined Google around the same time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:49&lt;/strong&gt; · RODY DAVIS: Yup, it&apos;ll be five years for me at Google too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:51&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:51&lt;/strong&gt; · MARK THOMPSON: Yeah, so five years at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:53&lt;/strong&gt; · Five years ago, joined the Angular team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:54&lt;/strong&gt; · Isn&apos;t that something?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:54&lt;/strong&gt; · RODY DAVIS: That&apos;s wild.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:55&lt;/strong&gt; · Yeah, congrats.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:56&lt;/strong&gt; · RODY DAVIS: Yeah, same to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:57&lt;/strong&gt; · I mean, five years is a big deal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:59&lt;/strong&gt; · Big deal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:59&lt;/strong&gt; · &amp;quot;Question: would Firebase Studio be able to build the Angular source repo?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:04&lt;/strong&gt; · Bazel in WSL is really tricky.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:08&lt;/strong&gt; · Oh, I see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:09&lt;/strong&gt; · RODY DAVIS: So there&apos;s a really interesting project out there that I&apos;ve been exploring, which is Nix plus Bazel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:15&lt;/strong&gt; · So NixOS defines all the binaries, and Bazel is really good at building application modules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:24&lt;/strong&gt; · And there&apos;s actually a project out there that you can search that combines Nix and Bazel together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:29&lt;/strong&gt; · And we&apos;re trying to see if there&apos;s a possibility of using that in Firebase Studio because that could be the best of both worlds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:36&lt;/strong&gt; · But yeah, we see a lot of people build-- you can build pretty big code bases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:39&lt;/strong&gt; · I think it&apos;s 100 gigs or something like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:42&lt;/strong&gt; · MARK THOMPSON: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:43&lt;/strong&gt; · OK, hold on one second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:44&lt;/strong&gt; · Somebody&apos;s like, hey, where&apos;s my answer?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:45&lt;/strong&gt; · OK, let me answer your question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:47&lt;/strong&gt; · &amp;quot;Since Angular 20 implemented zoneless and signal, is changedection onpush still required on components for performance?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:52&lt;/strong&gt; · Yes, sorry about that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:53&lt;/strong&gt; · You asked this question very patiently a long time ago.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:56&lt;/strong&gt; · I did not get to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:56&lt;/strong&gt; · So zoneless is in developer preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:59&lt;/strong&gt; · Most of our signal APIs are in stable mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:04&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:05&lt;/strong&gt; · Change detection onpush, I would say this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:10&lt;/strong&gt; · Required for performance, I mean, I think that&apos;s just a community standard at this point, that people think this is the best way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:16&lt;/strong&gt; · And so yes, so you can still use it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:18&lt;/strong&gt; · Now, what you&apos;re really asking about is, did we get to the point of the signal component yet?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:23&lt;/strong&gt; · Which is like, signal, fine grained reactivity, all the way down and really driving change detection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:30&lt;/strong&gt; · That&apos;s still on forthcoming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:33&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:33&lt;/strong&gt; · &amp;quot;Congratulations, Mark.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:34&lt;/strong&gt; · You&apos;ve been great to this community and face of Angular resurgence.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:36&lt;/strong&gt; · Hey, listen, I work on an amazing team with a lot of amazing people who work really hard, and I am grateful that I get to be a part of it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:43&lt;/strong&gt; · So really, really, really happy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:47&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:47&lt;/strong&gt; · &amp;quot;Were you using Angular since V1?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:49&lt;/strong&gt; · Believe it or not, believe it or not, my career trajectory is really interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:53&lt;/strong&gt; · I was a Java developer writing JavaBeans and J2EE code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:58&lt;/strong&gt; · Then, I saw Angular V1, and I was like, this is the future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:01&lt;/strong&gt; · So I switched my whole path from a back end engineer to a front end engineer with Angular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:06&lt;/strong&gt; · I worked on Angular for years up until Angular 2 came out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:09&lt;/strong&gt; · And then, I was working at a financial org.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:12&lt;/strong&gt; · Then, after that, I switched to React for a while, and I was actually teaching React.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:17&lt;/strong&gt; · And then, while I was teaching React, I wanted to make a mobile app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:20&lt;/strong&gt; · And then, I was like, oh, what should I use?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:22&lt;/strong&gt; · Should I use React, React Native?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:26&lt;/strong&gt; · Should I use this other thing called Titanium, which does not exist anymore?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:30&lt;/strong&gt; · A tool called Ruby Motion?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:32&lt;/strong&gt; · And then, this thing called Flutter came out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:35&lt;/strong&gt; · And then, that&apos;s how I got into the Flutter community, was because I was building a startup and I wanted to build a mobile app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:41&lt;/strong&gt; · And then, when I joined Google, I ended up, like I said, not getting that manager job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:45&lt;/strong&gt; · [LAUGHS] RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:46&lt;/strong&gt; · [LAUGHS] MARK THOMPSON: Which I&apos;m glad I didn&apos;t.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:47&lt;/strong&gt; · RODY DAVIS: Same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:47&lt;/strong&gt; · MARK THOMPSON: I mean, the Flutter manager is actually excellent, and he is the right person for that job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:52&lt;/strong&gt; · And then, back in the Angular community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:56&lt;/strong&gt; · All right, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:57&lt;/strong&gt; · &amp;quot;I was trying to install dotnet SDK in IDX through Nix, but it doesn&apos;t allow it.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:02&lt;/strong&gt; · Do you know anything about this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:03&lt;/strong&gt; · RODY DAVIS: You should check out our-- we have a .net template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:06&lt;/strong&gt; · So you can-- MARK THOMPSON: Oh yeah, I recall that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:08&lt;/strong&gt; · RODY DAVIS: --try it out, and that should have all the configuration right in these clips.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:12&lt;/strong&gt; · MARK THOMPSON: Mm!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:12&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:13&lt;/strong&gt; · MARK THOMPSON: Wait, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:14&lt;/strong&gt; · Can I just say what I love about this idea of Firebase Studio?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:18&lt;/strong&gt; · OK, I&apos;m going to go back real quick.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:19&lt;/strong&gt; · So we did our project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:20&lt;/strong&gt; · I&apos;m going to save this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:21&lt;/strong&gt; · It&apos;s not going to go anywhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:23&lt;/strong&gt; · If I were to look down here, here&apos;s what I really love.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:27&lt;/strong&gt; · You see all these things down here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:29&lt;/strong&gt; · RODY DAVIS: Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:30&lt;/strong&gt; · MARK THOMPSON: There&apos;s Java, Python, .NET, Android Studio, so you can do some mobile development, Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:35&lt;/strong&gt; · OK, next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:36&lt;/strong&gt; · Node, Genkit, Astro, all this stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:39&lt;/strong&gt; · Here&apos;s why this got me really excited, friends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:41&lt;/strong&gt; · Again, let&apos;s get back to our engineering brains.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:43&lt;/strong&gt; · If you want to learn something new that you&apos;ve been trying, you&apos;ve been really interested in, and you&apos;re like, ah, but I don&apos;t really have-- I don&apos;t want to do the setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:50&lt;/strong&gt; · I don&apos;t want to do the setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:51&lt;/strong&gt; · I never want to do the setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:52&lt;/strong&gt; · RODY DAVIS: Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:52&lt;/strong&gt; · MARK THOMPSON: Like, jump in right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:54&lt;/strong&gt; · That&apos;s why I go to firebase.studio, and just start coding in that with one of these.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:01&lt;/strong&gt; · That&apos;s wild to think about you could do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:03&lt;/strong&gt; · RODY DAVIS: Yeah, it&apos;s like try before you buy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:04&lt;/strong&gt; · You want to see if you&apos;re going to love it before you set it up locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:07&lt;/strong&gt; · MARK THOMPSON: And wait.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:08&lt;/strong&gt; · And then, you can even get some templates too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:11&lt;/strong&gt; · There&apos;s .NET template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:12&lt;/strong&gt; · I can click on this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:13&lt;/strong&gt; · Let me just click on this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:13&lt;/strong&gt; · Let me see what happens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:14&lt;/strong&gt; · RODY DAVIS: We even have a lot of people using the Laravel template as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:17&lt;/strong&gt; · That&apos;s been cool to see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:18&lt;/strong&gt; · MARK THOMPSON: Ooh, I don&apos;t know what any of this means.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:20&lt;/strong&gt; · I&apos;m just going to click Blazor App.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:22&lt;/strong&gt; · RODY DAVIS: Yeah, this should use the WASM build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:24&lt;/strong&gt; · MARK THOMPSON: OK, sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:25&lt;/strong&gt; · Because I&apos;m like, I don&apos;t know what any of that means.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:26&lt;/strong&gt; · You can&apos;t get me to-- I don&apos;t really know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:28&lt;/strong&gt; · But this is interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:29&lt;/strong&gt; · RODY DAVIS: Yeah, I would definitely suggest if there is a stack you&apos;re trying to use and trying to import your existing projects, go to one of our templates that usually matches it and see if you can copy over the Nix files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:40&lt;/strong&gt; · If you&apos;re running into issues of something that doesn&apos;t exist, reach out and we can see if we can make a new template or guide you through that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:46&lt;/strong&gt; · Our forums are really popular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:48&lt;/strong&gt; · And then, also, we&apos;re in the Firebase community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:51&lt;/strong&gt; · MARK THOMPSON: Yeah, no.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:52&lt;/strong&gt; · Amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:53&lt;/strong&gt; · Amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:54&lt;/strong&gt; · Again, I know it sounds like, well, of course you think It&apos;s amazing, Mark.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:57&lt;/strong&gt; · You work at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:58&lt;/strong&gt; · But-- RODY DAVIS: It&apos;s nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:00&lt;/strong&gt; · MARK THOMPSON: --I wouldn&apos;t have invited Rody if I didn&apos;t like think that this was awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:03&lt;/strong&gt; · I think this is beneficial and it&apos;s interesting to our community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:06&lt;/strong&gt; · So, look at all this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:08&lt;/strong&gt; · So now, if I knew how to write some .NET, which I used to know-- RODY DAVIS: Yep, same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:13&lt;/strong&gt; · MARK THOMPSON: --I would like-- oh, look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:15&lt;/strong&gt; · And you can install this right off the bat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:16&lt;/strong&gt; · OK, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:18&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:18&lt;/strong&gt; · RODY DAVIS: Sometimes you&apos;ll see that pop up if there&apos;s a race condition of installing the extensions, but yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:24&lt;/strong&gt; · MARK THOMPSON: All right, so my app can run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:26&lt;/strong&gt; · How do I preview this, in the terminal?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:29&lt;/strong&gt; · RODY DAVIS: You can do the IDX, dev.next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:31&lt;/strong&gt; · It should show if the preview is selected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:37&lt;/strong&gt; · Let&apos;s move that over to-- yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:40&lt;/strong&gt; · MARK THOMPSON: Oh, something&apos;s on the web.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:41&lt;/strong&gt; · Something&apos;s happening.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:42&lt;/strong&gt; · RODY DAVIS: Yeah, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:42&lt;/strong&gt; · It is going to be web preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:43&lt;/strong&gt; · So you can just close it, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:44&lt;/strong&gt; · MARK THOMPSON: Oh, right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:45&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:46&lt;/strong&gt; · Something on the web is about to happen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:47&lt;/strong&gt; · Pretty cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:49&lt;/strong&gt; · But yeah, so if you want to try this, this is just something available to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:53&lt;/strong&gt; · But I&apos;m super, super excited about all of this stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:58&lt;/strong&gt; · I just think there&apos;s just so much cool stuff happening.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:01&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:02&lt;/strong&gt; · &amp;quot;Good thing Mark found Flutter and didn&apos;t run into Xamarin.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:05&lt;/strong&gt; · Hey, listen, Xamarin was on my list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:07&lt;/strong&gt; · RODY DAVIS: Same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:08&lt;/strong&gt; · MARK THOMPSON: The only reason I didn&apos;t try Xamarin at the time, # is that I think .NET and C#, they also had an interesting conflict of people were trying to figure out what&apos;s the right version of this, what&apos;s the right tool to put it all together?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:22&lt;/strong&gt; · And it was a little confusing for me at the time, but I still think it&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:25&lt;/strong&gt; · I just didn&apos;t get to start it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:26&lt;/strong&gt; · RODY DAVIS: For me, I was working at a .NET shop when I found out about Flutter, and the first thing was like, well, why not Xamarin?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:32&lt;/strong&gt; · And for me, it was actually, I don&apos;t think our server could have handled all the mobile apps versus the few amount of web apps that they had.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:44&lt;/strong&gt; · So they were expecting a certain amount of scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:46&lt;/strong&gt; · So that&apos;s actually how I found out about Firebase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:48&lt;/strong&gt; · So we could store a lot of our collections in Firestore, scale out, and then basically, just sync with the API server on .NET.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:57&lt;/strong&gt; · So yeah, obviously, there&apos;s different tools for different things, but yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:02&lt;/strong&gt; · MARK THOMPSON: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:03&lt;/strong&gt; · Oh wait.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:03&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:03&lt;/strong&gt; · So we&apos;re out of time with our show, but this actually worked though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:07&lt;/strong&gt; · RODY DAVIS: Yeah, there it is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:08&lt;/strong&gt; · MARK THOMPSON: There it is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:10&lt;/strong&gt; · They got a Hello World app here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:11&lt;/strong&gt; · I&apos;m going to close this down for a second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:13&lt;/strong&gt; · Yeah, so there you go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:14&lt;/strong&gt; · So, I mean, sure, there&apos;s some things that we can do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:17&lt;/strong&gt; · I don&apos;t know if this is required, asset to build and debug.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:19&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:20&lt;/strong&gt; · I&apos;m not going to do that because I don&apos;t know what I&apos;m doing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:22&lt;/strong&gt; · But the idea that this exists, and in our conversation, I&apos;m able to boot up a .NET.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:26&lt;/strong&gt; · I can go to a tutorial in a different tab right now-- RODY DAVIS: Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:29&lt;/strong&gt; · MARK THOMPSON: --and then, I can just start learning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:31&lt;/strong&gt; · That&apos;s just pretty rad.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:32&lt;/strong&gt; · This is pretty rad.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:34&lt;/strong&gt; · OK, last question, and then I think we&apos;re going to call it for today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:38&lt;/strong&gt; · And this is not the last time we&apos;re going to have Rody on, because this has been amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:41&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:41&lt;/strong&gt; · MARK THOMPSON: &amp;quot;Mark, I&apos;ve been using GitHub Copilot in VS Code since sometime.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:44&lt;/strong&gt; · This is really promising.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:46&lt;/strong&gt; · Yeah, Ravi Kumar, I&apos;m so glad that you are finding some opportunities here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:53&lt;/strong&gt; · Again, what we care about is that you build great applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:56&lt;/strong&gt; · That&apos;s what we care about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:57&lt;/strong&gt; · RODY DAVIS: Totally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:57&lt;/strong&gt; · MARK THOMPSON: How you do it is up to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:59&lt;/strong&gt; · OK?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:59&lt;/strong&gt; · Build cool stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:01&lt;/strong&gt; · I&apos;m glad that this looks cool to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:03&lt;/strong&gt; · All right, friends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:04&lt;/strong&gt; · Definitely, let&apos;s show a lot of love to Rody in the chat, because if you do, maybe he&apos;ll come back and show us that compressed RAG thing, workflow he was talking about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:13&lt;/strong&gt; · I would love to see that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:14&lt;/strong&gt; · I would love to have a stream where you just show them-- RODY DAVIS: Right on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:16&lt;/strong&gt; · MARK THOMPSON: --how to use it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:18&lt;/strong&gt; · So show a lot of love in the chat so he&apos;ll come back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:20&lt;/strong&gt; · Rody, thank you for hanging out with us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:21&lt;/strong&gt; · It has been a blast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:22&lt;/strong&gt; · RODY DAVIS: Yeah, thanks for having me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:23&lt;/strong&gt; · This was awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:24&lt;/strong&gt; · MARK THOMPSON: Yeah, all right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:25&lt;/strong&gt; · All right, friends at home, thank you for being you, thank you for being part of Angular community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:29&lt;/strong&gt; · # And listen, whether you use Angular, React, Vue, C#, whatever it is, it&apos;s OK, because all I care about, all we care about, is that you get out there and you build great apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:38&lt;/strong&gt; · RODY DAVIS: Yeah, and it&apos;s firebase.studio for the URL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:41&lt;/strong&gt; · And then, we even have an Angular template that you can share with your friends too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:45&lt;/strong&gt; · MARK THOMPSON: And the Angular template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:46&lt;/strong&gt; · You know what?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:47&lt;/strong&gt; · Before I forget, let me put the firebase.studio in the chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:50&lt;/strong&gt; · OK, firebase.studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:51&lt;/strong&gt; · RODY DAVIS: And then, the Angular template&apos;s studio.firebase. google.com/news/angular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:57&lt;/strong&gt; · MARK THOMPSON: Let&apos;s go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:58&lt;/strong&gt; · Let&apos;s go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:59&lt;/strong&gt; · Everything&apos;s there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33:00&lt;/strong&gt; · All right, friends, we&apos;ll catch you the next time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33:02&lt;/strong&gt; · RODY DAVIS: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33:03&lt;/strong&gt; · MARK THOMPSON: Be awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33:04&lt;/strong&gt; · RODY DAVIS: Bye.&lt;/p&gt;
</content:encoded></item><item><title>Compile SQLite from source to WASM in Firebase Studio</title><link>https://rodydavis.com/videos/firebase-studio/compile-sqlite-from-source-to-wasm-in-firebase-studio/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/compile-sqlite-from-source-to-wasm-in-firebase-studio/</guid><description>A step-by-step tutorial on using Firebase Studio, Nix, and Gemini to clone code, manage dependencies, and compile SQLite from source to WebAssembly (WASM).</description><pubDate>Wed, 14 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Compile SQLite from source to WASM in Firebase Studio&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;hox-Sv8ZHxE&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Rody, a Developer Relations Engineer for Firebase Studio shares how easy it is to get started with a blank template in Firebase Studio. In this video, he shows how to clone your code inside a workstation and use nix to install dependencies. Watch a step by step tutorial on cloning code, setting dev.nix, web preview, and leverage Gemini in Firebase Studio.&lt;/p&gt;
&lt;p&gt;Chapters:
0:00 - Get started
1:16 - Cloning SQLite and Emscripten
1:42 - Set up the VM
3:47 - Configure web preview
5:15 - Leverage Gemini
5:47 - Recap&lt;/p&gt;
&lt;p&gt;Firebase Studio → https://goo.gle/firebasestudio&lt;/p&gt;
&lt;p&gt;#Firebase #SQLite #WASM #NixOS&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned:,  Firebase,&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Get started&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi, my name is Rody, DevRel for Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:07&lt;/strong&gt; · And today, I want to show you how easy it is to get started with our blank template, where you can clone your code and get started with Nix.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · The first step is to go to studio.firebase.google.com, and then click on New Workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · On the top right, you&apos;ll see Add New Empty Workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:23&lt;/strong&gt; · And when we click that, we&apos;re brought to our new workspace creation page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:26&lt;/strong&gt; · Here you can add any name for your workspace and not having to worry about name collisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:31&lt;/strong&gt; · In this case, we&apos;ll just do App, and then hit Create.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:37&lt;/strong&gt; · After our workspace is created, you will see the readme, dev.nix, and Gemini Chat all come up at the same time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · Inside of dev.nix, this is where we can define all of our dependencies for our code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:49&lt;/strong&gt; · This is something that is configured at the OS level using NixOS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · We take advantage of Nix packages, which you can find on NixOS packages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:59&lt;/strong&gt; · We can have custom hooks to set up the web previews, as well as the onCreate and onStart hooks, which get run when the workspace is first created and every time that you reload the page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · Today, we&apos;re going to be compiling SQLite from source, compiling it to WASM, and setting it up with the web preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · So the first step is to go ahead and clone SQLite.&lt;/p&gt;
&lt;h3&gt;Cloning SQLite and Emscripten&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · We can open up the terminal and then clone SQLite from the Git mirror.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25&lt;/strong&gt; · The next step is we need to clone Emscripten so we can compile SQLite to WASM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31&lt;/strong&gt; · So we can do that by running the following command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34&lt;/strong&gt; · Once that&apos;s done, we will have both SQLite and Emscripten cloned inside of our workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:41&lt;/strong&gt; · Next, we need to set up our VM so that it has all the required dependencies so that we can compile SQLite and use both Python and C toolchains.&lt;/p&gt;
&lt;h3&gt;Set up the VM&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:50&lt;/strong&gt; · So here we can go to dev.nix and then uncomment the Python packages because that&apos;s actually what we need.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:58&lt;/strong&gt; · Here, above packages, you&apos;ll see this Add Packages button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01&lt;/strong&gt; · This will open up the NixOS package registry, which we can use to search for packages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:07&lt;/strong&gt; · In this case, we need GCC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:09&lt;/strong&gt; · And in just a second, we&apos;ll see that it pulls all of the packages related to that name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:14&lt;/strong&gt; · We can go ahead and add that to our packages array.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:17&lt;/strong&gt; · We also need to add gnumake, which we can also do by searching for gnumake and then clicking on the first one in the list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:24&lt;/strong&gt; · After that&apos;s done, we will see that this button on the bottom right says Rebuild Environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:29&lt;/strong&gt; · This is what Firebase Studio has to do every time that we update dev.nix.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:34&lt;/strong&gt; · Since it&apos;s an immutable way to specify our dependencies, it&apos;ll create a new snapshot that will rebuild the new environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · And if there&apos;s any errors, we can roll back to the previous version.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · This usually just takes a couple seconds, so we can wait for that to complete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:49&lt;/strong&gt; · After that&apos;s done, we now have Python, GCC, and gnumake all set up and registered in our VM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:57&lt;/strong&gt; · This means that it&apos;s available both in the terminal and in the web previews.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:01&lt;/strong&gt; · Let&apos;s first go ahead and build Emscripten from source, which we can do so by navigating to the directory and then running the following command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:10&lt;/strong&gt; · After Emscripten finishes installing, we now can activate it, which we can do so by modifying the command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · Next, we can copy this line, which we will paste into the terminal, which will make it available for the current terminal session.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:24&lt;/strong&gt; · Next, we need to compile SQLite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:27&lt;/strong&gt; · So we can do that by navigating up and into the SQLite directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:31&lt;/strong&gt; · The first step is to configure and enable all the files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:38&lt;/strong&gt; · Next, we need to create the SQLite C file, which we can do by running make sqllite3.c.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:46&lt;/strong&gt; · After that&apos;s done, it&apos;s time to configure the web preview to use the JSFiddle that is included inside the SQLite source code.&lt;/p&gt;
&lt;h3&gt;Configure web preview&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:54&lt;/strong&gt; · To do so, head over to the dev.nix, and navigate down to the Preview section.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:59&lt;/strong&gt; · We can go ahead and uncomment this preview block, but we need to change it for our settings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:07&lt;/strong&gt; · First, we need to add a change working directory so that the preview is actually started in the correct subfolder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:13&lt;/strong&gt; · Everything in Nix requires a semicolon to be at the end, so make sure not to forget that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:18&lt;/strong&gt; · And since we already have Python installed on our VM, we can actually take advantage of the Python static HTTP server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:24&lt;/strong&gt; · In Firebase Studio, all previews run on a specific port, so make sure to pass that in with an uppercase PORT to your server command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:33&lt;/strong&gt; · And after we do that, we just have to rebuild our environment one more time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:38&lt;/strong&gt; · After our IDE rebuilds, we can then go to the Web tab, after which we&apos;ll see this JSFiddle up and running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:45&lt;/strong&gt; · What&apos;s really cool about this is it&apos;s using the SQLite WASM build that we just compiled, which also depended on Emscripten, all with their own local environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:55&lt;/strong&gt; · To verify that this is working, we can just write a simple SQL query.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:08&lt;/strong&gt; · After we do it, we can hit Run, and we can see Hello Firebase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:12&lt;/strong&gt; · All right, this is awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:14&lt;/strong&gt; · Now let&apos;s use Gemini to update files in our project.&lt;/p&gt;
&lt;h3&gt;Leverage Gemini&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:17&lt;/strong&gt; · When you click over on the Gemini tab, you&apos;ll see that there is a model picker where you can choose anything from Gemini 2.5 Pro to Flash and other models, even specifying a custom model ID.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:27&lt;/strong&gt; · We can also include attachments here with the bottom left icon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:31&lt;/strong&gt; · But in this case, we want to update our readme with the current state of our project, so we can just ask Gemini to do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:38&lt;/strong&gt; · We can go ahead and give it a simple prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:40&lt;/strong&gt; · And after it&apos;s done, it&apos;s going to suggest an edit, which we then can review or just accept.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:46&lt;/strong&gt; · So it looks like everything&apos;s coming together.&lt;/p&gt;
&lt;h3&gt;Recap&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:48&lt;/strong&gt; · We cloned both Emscripten and SQLite from source and compiled them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:52&lt;/strong&gt; · We then set up our dev.nix to have all of the dependencies for our NixOS environment set up, which we can then go and review in dev.nix.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:01&lt;/strong&gt; · We also set up the web preview to be able to run the actual SQLite WASM file inside of the web preview using that special port.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:10&lt;/strong&gt; · And then we leveraged Gemini to be able to read our files and run terminal commands.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:15&lt;/strong&gt; · I hope this gives you a brief overview of the capabilities of Firebase Studio and how easy it is to get started with a blank template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:22&lt;/strong&gt; · If you have any questions, make sure to reach out and let us know down in the comments if you have anything that you&apos;d like to see next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:28&lt;/strong&gt; · Thanks for watching.&lt;/p&gt;
</content:encoded></item><item><title>Explore all the official templates for Firebase Studio</title><link>https://rodydavis.com/videos/firebase-studio/explore-all-the-official-templates-for-firebase-studio/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/explore-all-the-official-templates-for-firebase-studio/</guid><description>Firebase Studio offers a comprehensive template library for web, backend, mobile, and ML development, supporting popular frameworks like React, Angular, Next.JS, Flutter, and databases like Firestore.</description><pubDate>Fri, 09 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Explore all the official templates for Firebase Studio&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;vWfoxcDETY0&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Check out and learn how to use one of our many templates for Firebase Studio covering lots of popular frameworks and languages.&lt;/p&gt;
&lt;p&gt;Firebase Studio → https://goo.gle/firebasestudio&lt;/p&gt;
&lt;p&gt;#Firebase&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned: Firebase,&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · RODY DAVIS: Hi, my name is Rody, devro for Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:03&lt;/strong&gt; · And we have a really big template library for you to check out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:05&lt;/strong&gt; · To get started, go to studio.firebase.google.com and then click on Add Workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · Our template library includes a lot of different options for you, whether you work on the web, backend, frontend, or mobile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:19&lt;/strong&gt; · Web, for example, we have Angular, React, Next.JS, Astro, Svelte, and many other templates that you may be familiar with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:27&lt;/strong&gt; · If you&apos;re looking for something on the backend, we support Go, Python, Java, .NET, Node, Laravel, and so many more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · If you&apos;re looking for mobile, we have Flutter, React Native, and even Android Studio on the Cloud.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:40&lt;/strong&gt; · If you&apos;re an ML developer, we also support the Gemini API and Genkit, as well as LangChain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · And of course, if you&apos;re working with databases, we have Postgres, MongoDB, MySQL, Prisma, and even Data Connect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:53&lt;/strong&gt; · And if you don&apos;t find what you&apos;re looking for, you can actually get started with an Empty Workspace, which is powered by NixOS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · And you can configure everything based on your current project requirements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · To check out all the templates, you can check it out at studio.firebase. google.com/templates.&lt;/p&gt;
</content:encoded></item><item><title>Firebase Studio now supports Gemma 3 image inputs on the Gemini Developer API 🔥🪄🚀</title><link>https://rodydavis.com/videos/firebase-studio/firebase-studio-now-supports-gemma-3-image-inputs-on-the-gemini-developer-api-/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/firebase-studio-now-supports-gemma-3-image-inputs-on-the-gemini-developer-api-/</guid><description>Firebase Studio now enables developers to utilize Gemma 3 models with image inputs directly through the Gemini Developer API.</description><pubDate>Fri, 09 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Firebase Studio now supports Gemma 3 image inputs on the Gemini Developer API 🔥🪄🚀&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;tndZqTcQWK4&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Try it out on the Gemini template and switch to the gemma-27b-it model!
https://studio.firebase.google.com/new/gemini&lt;/p&gt;
&lt;p&gt;Learn more about Gemma 3:
https://ai.google.dev/gemma/docs/core&lt;/p&gt;
&lt;p&gt;Website: https://rodydavis.com
Twitter: @rodydavis
Github: @rodydavis
Patreon: https://www.patreon.com/rodydavis
Buy Me A Coffee: https://www.buymeacoffee.com/3mgFNYd&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Did you know that Firebase Studio now supports Gemma 3 with image input? You can get started by going to Firebase Studio and then going to new workspace AI and ML and get started with the Gemini API. Make sure to select JavaScript web app with V. Give a name for your workspace and hit create. Once the workspace is loaded, you can create an API key by opening up the Firebase Studio extension and then clicking on the Gemini integration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:29&lt;/strong&gt; · You can generate an API key and you don&apos;t even have to leave Firebase Studio. Once it&apos;s generated, you can copy it and paste it inside of the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · By default, the template uses Gemini 1.5 Flash. And we can test that it&apos;s working by clicking on an image and hitting go. This will create a recipe based on the image and generate a markdown version of that recipe. But we can easily swap this out for Gemma. So go ahead to the model string and replace it with Gemma 327B instruction tuned. We can verify this is working by selecting an image and then hitting go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13&lt;/strong&gt; · Try it out for yourself at studio.firebase.google.com/new/jgemini.&lt;/p&gt;
</content:encoded></item><item><title>Firebase After Hours #14: Live Vibe Coding with Firebase Studio</title><link>https://rodydavis.com/videos/firebase-studio/firebase-after-hours-14-live-vibe-coding-with-firebase-studio/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/firebase-after-hours-14-live-vibe-coding-with-firebase-studio/</guid><description>Developers join Firebase After Hours #14 for a live &quot;vibe coding&quot; session where experts build apps using Firebase Studio and answer community questions about the platform.</description><pubDate>Thu, 31 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Firebase After Hours #14: Live Vibe Coding with Firebase Studio&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;wbrI2egh7bQ&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Join @PeterFriese and @NoheDev  on Firebase After Hours as they vibe code an app (or two) using Firebase Studio. @RodyDavis (Developer Relations Engineer on Firebase Studio) will be their guest. Bring your questions about Firebase Studio, and they&apos;ll answer them live!&lt;/p&gt;
&lt;p&gt;Resources:
From vibe to reality: Integrating Firebase in Firebase Studio → https://goo.gle/4lWAXf8
Forum: → https://community.firebasestudio.dev/&lt;/p&gt;
&lt;p&gt;#Firebase #FirebaseAfterHours #FirebaseStudio #Streaming #GoogleCloud #LiveStream #DeveloperCommunity&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:59&lt;/strong&gt; · PETER FRIESE: Hello, everybody and welcome to &amp;quot;Firebase After Hours,&amp;quot; number 14.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06&lt;/strong&gt; · Can you believe it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07&lt;/strong&gt; · Number 14.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · When we started doing this, I had no idea that we would end up at a number like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · And we have no intention of stopping there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17&lt;/strong&gt; · So welcome to the stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · We&apos;ve got something really exciting for you today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · But before we can get started, let&apos;s do the housekeeping.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27&lt;/strong&gt; · So I want to remind everybody to be nice to each other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31&lt;/strong&gt; · We have a live stream going and a live chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34&lt;/strong&gt; · So you can use the live chat to communicate with us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · We want you to send us your questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:40&lt;/strong&gt; · So please send questions that you want us to answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:43&lt;/strong&gt; · And we will take the ones that make sense in the context of this live stream, which will be about Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:51&lt;/strong&gt; · And if there are any other ones that we can also answer and which we think might be interesting for folks on stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:59&lt;/strong&gt; · But in the interest of everybody, let&apos;s be polite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:05&lt;/strong&gt; · And if you hate something, then maybe not say it here, but say it into your pillow or into a bucket and say the nice things to us and be constructive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:19&lt;/strong&gt; · With that out of the way-- oh, and I also see people are using the emojis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:25&lt;/strong&gt; · So please use the emojis if you like something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:27&lt;/strong&gt; · Press the heart emoji or the party popper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:31&lt;/strong&gt; · That&apos;s always great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · All right, so with that out of the way, let me bring on my most favorite co-host, Nohe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · Here he is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:42&lt;/strong&gt; · Nohe, how&apos;s it going?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:43&lt;/strong&gt; · ALEXANDER NOHE: It&apos;s good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:44&lt;/strong&gt; · I&apos;m glad I made it to the most favorite ranking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:47&lt;/strong&gt; · PETER FRIESE: [LAUGHS] You are my most favorite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:49&lt;/strong&gt; · ALEXANDER NOHE: Well, you have so many to pick from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:51&lt;/strong&gt; · PETER FRIESE: [LAUGHS] Man, I haven&apos;t seen you for such a long time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:56&lt;/strong&gt; · What&apos;s happening?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:58&lt;/strong&gt; · ALEXANDER NOHE: Have you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:00&lt;/strong&gt; · PETER FRIESE: Have you-- I mean, I saw you five minutes ago.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:04&lt;/strong&gt; · ALEXANDER NOHE: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:05&lt;/strong&gt; · Well, I mean-- well, weren&apos;t you also on vacation?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:08&lt;/strong&gt; · I did a vacation last year.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:10&lt;/strong&gt; · PETER FRIESE: Yeah, but we were on different vacations, so I was on an island in the north of Germany.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:16&lt;/strong&gt; · And you probably-- I didn&apos;t see you there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · So I assumed you were somewhere else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · ALEXANDER NOHE: That&apos;s the most details you gave me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:20&lt;/strong&gt; · So I think I went to a different island.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:22&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:23&lt;/strong&gt; · That&apos;s why I think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:24&lt;/strong&gt; · [LAUGHS] Which island did you go to?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:28&lt;/strong&gt; · ALEXANDER NOHE: No, I went to Wyoming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:33&lt;/strong&gt; · I went to Yellowstone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:35&lt;/strong&gt; · It&apos;s one of the national parks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:36&lt;/strong&gt; · Do you know why they call it Yellowstone?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:40&lt;/strong&gt; · PETER FRIESE: I haven&apos;t been there, but I hear great things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:42&lt;/strong&gt; · So next time I come, maybe we can go together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:46&lt;/strong&gt; · ALEXANDER NOHE: Yeah, yeah, we&apos;ll take a little road trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:49&lt;/strong&gt; · I saw a bear, some moose, some elk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:53&lt;/strong&gt; · PETER FRIESE: OK, wow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:53&lt;/strong&gt; · That sounds exciting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:55&lt;/strong&gt; · How far away were you from the bear?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:58&lt;/strong&gt; · ALEXANDER NOHE: So the bear was the closest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:00&lt;/strong&gt; · I got maybe 10 feet away.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:02&lt;/strong&gt; · It was a brown baby bear.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:05&lt;/strong&gt; · PETER FRIESE: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:06&lt;/strong&gt; · ALEXANDER NOHE: It was not-- the bear walked up to me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:09&lt;/strong&gt; · I didn&apos;t walk up to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:11&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:13&lt;/strong&gt; · But we were just hiking along.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:14&lt;/strong&gt; · PETER FRIESE: Now it sounds a little bit more scary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:17&lt;/strong&gt; · 10 feet, that&apos;s not very far.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:18&lt;/strong&gt; · ALEXANDER NOHE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:19&lt;/strong&gt; · And then we had to climb over rocks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:21&lt;/strong&gt; · I dropped a camera lens that I was borrowing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:23&lt;/strong&gt; · So I actually broke the lens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:25&lt;/strong&gt; · PETER FRIESE: Oh, no.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:26&lt;/strong&gt; · That sounds like an expensive vacation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:28&lt;/strong&gt; · ALEXANDER NOHE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:29&lt;/strong&gt; · Well, it turned out to be one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:32&lt;/strong&gt; · Yeah, but it was good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:34&lt;/strong&gt; · How about-- how was your island vacation in the north of Germany?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:37&lt;/strong&gt; · PETER FRIESE: Yeah, it was really good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:38&lt;/strong&gt; · So we were on an island called Used, and it was absolutely fantastic weather.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:45&lt;/strong&gt; · So we had sunshine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:46&lt;/strong&gt; · We were hanging around on the beach, doing bicycle rides.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:52&lt;/strong&gt; · So yeah, it was a great time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:55&lt;/strong&gt; · Very relaxing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:57&lt;/strong&gt; · No bears, but we had a seagull.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:59&lt;/strong&gt; · So the seagulls.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:01&lt;/strong&gt; · Man, I tell you, we only had a jar of marmalade on our balcony.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:06&lt;/strong&gt; · And then, next thing you know, this giant seagull lands on the balcony, trying to peck on the marmalade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:14&lt;/strong&gt; · [LAUGHTER] Well-- all right, so we&apos;re not here to talk about vacation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:22&lt;/strong&gt; · We&apos;re here to talk about Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:26&lt;/strong&gt; · And we have a guest that knows everything about Firebase Studio, at least I hope.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:34&lt;/strong&gt; · So here he is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:36&lt;/strong&gt; · RODY DAVIS: Hey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:36&lt;/strong&gt; · PETER FRIESE: Rody, hey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:38&lt;/strong&gt; · How is it going?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:39&lt;/strong&gt; · RODY DAVIS: Going good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:40&lt;/strong&gt; · Yeah, super excited to come and code with y&apos;all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:43&lt;/strong&gt; · And I feel like this will be a really fun stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:46&lt;/strong&gt; · PETER FRIESE: Yeah, absolutely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:48&lt;/strong&gt; · So maybe we can start, and you can tell us what you do, what&apos;s your role, and maybe a fun fact about you that you&apos;re happy to share.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:58&lt;/strong&gt; · RODY DAVIS: Yeah, so-- yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:01&lt;/strong&gt; · My name is Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:02&lt;/strong&gt; · I am a DevRel on Firebase Studio, so helping people get access to our huge selection of templates, but also working across teams to make sure that everybody has a good experience there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:17&lt;/strong&gt; · And it&apos;s been interesting with the advent of vibe coding, where now the audience is changing, too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:24&lt;/strong&gt; · You have people that have no experience with coding at all, all the way up to super experienced devs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:30&lt;/strong&gt; · And, more recently, I feel like I&apos;ve been seeing a lot of people come back into coding later in their career.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:36&lt;/strong&gt; · And now you have people that, oh, I used to do it, like, 15, 20 years ago, and I&apos;m getting back into building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:42&lt;/strong&gt; · So yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:43&lt;/strong&gt; · And then a fun fact, I used to be an audio engineer before I was a software engineer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:48&lt;/strong&gt; · PETER FRIESE: Oh, is that why your audio signal sounds so good?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:53&lt;/strong&gt; · RODY DAVIS: Yes, that&apos;s why I have a proper mixing board to the left of me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:57&lt;/strong&gt; · PETER FRIESE: Nice, nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:58&lt;/strong&gt; · I feel I need to spend some quality time with you so you can help me fix the audio on my stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:04&lt;/strong&gt; · RODY DAVIS: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:05&lt;/strong&gt; · For sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:07&lt;/strong&gt; · PETER FRIESE: All right, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:08&lt;/strong&gt; · So usually we do a game of what is your favorite indentation method, but before we started the stream, we were talking about food or vacation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:20&lt;/strong&gt; · So how about we take a new question, and instead of doing tabs versus spaces, how about we do, what is your favorite food?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:30&lt;/strong&gt; · And why is it pasta?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:31&lt;/strong&gt; · No, sorry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:32&lt;/strong&gt; · So what&apos;s your favorite food?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:33&lt;/strong&gt; · [LAUGHS] RODY DAVIS: Yeah, I think one of my favorite ones is sushi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:39&lt;/strong&gt; · But if I had to pick a specific thing inside there, probably, like, takoyaki from Japan, like the little octopus balls they fry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:46&lt;/strong&gt; · Those are pretty good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:48&lt;/strong&gt; · PETER FRIESE: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:49&lt;/strong&gt; · Nohe, what about you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:51&lt;/strong&gt; · ALEXANDER NOHE: I really like Chicken Chesapeake.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:54&lt;/strong&gt; · PETER FRIESE: What&apos;s the second part?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:56&lt;/strong&gt; · Chesapeake?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:57&lt;/strong&gt; · ALEXANDER NOHE: Yeah, Chesapeake.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:58&lt;/strong&gt; · It&apos;s like a giant bay in North America.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:02&lt;/strong&gt; · There&apos;s like crabs that come out of the bay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:04&lt;/strong&gt; · RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:04&lt;/strong&gt; · ALEXANDER NOHE: Then we eat the crabs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:06&lt;/strong&gt; · So Chicken Chesapeake is chicken, and it&apos;s layered on top with a crab dip or other crab meat items.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:17&lt;/strong&gt; · It&apos;s pretty good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:18&lt;/strong&gt; · PETER FRIESE: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:18&lt;/strong&gt; · RODY DAVIS: Is it a full on boil, like a normal seafood boil?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:22&lt;/strong&gt; · ALEXANDER NOHE: You&apos;re thinking of like a low country boil.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · That&apos;s something different.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:26&lt;/strong&gt; · Well, I assume you&apos;re thinking of a low country boil, where you have the giant thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:30&lt;/strong&gt; · You throw in the crawfish and the corn and the potatoes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:33&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:34&lt;/strong&gt; · So you just cook the crabs and get them out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:37&lt;/strong&gt; · ALEXANDER NOHE: Well, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:38&lt;/strong&gt; · So you make the crab dip first, where it&apos;s-- it&apos;s not really crab dip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:43&lt;/strong&gt; · It&apos;s like a little bit of a crab cake blended with chicken on top.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:48&lt;/strong&gt; · So you get the very filling chicken and then however much crab you picked off-- RODY DAVIS: That&apos;s nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:55&lt;/strong&gt; · ALEXANDER NOHE: --or gotten from the jar or your cousin that runs the crab shack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:01&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:02&lt;/strong&gt; · RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:03&lt;/strong&gt; · How about you, Peter?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:04&lt;/strong&gt; · PETER FRIESE: So if you&apos;ve watched our last, most recent video, you might know that one of my favorite dishes is parmigiana.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:15&lt;/strong&gt; · And usually you make it with aubergine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:18&lt;/strong&gt; · But my favorite is zucchini.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:20&lt;/strong&gt; · So that would be my go-to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:23&lt;/strong&gt; · And anything that has tomato sauce in it, you can&apos;t go wrong with it, I think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:28&lt;/strong&gt; · It just is perfect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:30&lt;/strong&gt; · RODY DAVIS: That&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:31&lt;/strong&gt; · PETER FRIESE: So everybody on the stream, tell us what you think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:35&lt;/strong&gt; · What&apos;s your favorite dish?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:36&lt;/strong&gt; · I already saw that people mentioned chimichurri and stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:41&lt;/strong&gt; · So feel free to let us know what you like.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:45&lt;/strong&gt; · So let&apos;s talk about Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:49&lt;/strong&gt; · So one of the first questions that we should probably talk about is, what is it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:56&lt;/strong&gt; · And how is it different from Firebase?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:01&lt;/strong&gt; · RODY DAVIS: Yeah, that&apos;s a good question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:04&lt;/strong&gt; · Yeah, and also people may be wondering, well, how is this different than just VS Code on my computer too?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:10&lt;/strong&gt; · So Firebase Studio is, at its core, a computing platform that we enable and unlock people to use in the browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:19&lt;/strong&gt; · So we&apos;re built on top of NixOS, which is a VM that has a huge amount of packages in the Linux ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:27&lt;/strong&gt; · And because of that, we are able to support full server side, client side stacks, all running together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:36&lt;/strong&gt; · You can run Kubernetes or full Docker containers, all the way down to just a simple Go server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:44&lt;/strong&gt; · Really, it&apos;s about-- as long as you can configure it in Nix, you can usually get the template running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:49&lt;/strong&gt; · So Firebase Studio, which was previously Project IDX, wraps up all of that into a nice feature suite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:57&lt;/strong&gt; · But then, now part of Firebase, we are able to deeper integrate into creating Firebase services, like projects and API keys for Firebase AI Logic, and have deep relationships across Google to try to make that development experience better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:16&lt;/strong&gt; · And then, of course, at Google Cloud Next, when we launched, we also supported this new kind of natural language prompting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:24&lt;/strong&gt; · And that is kind of a new way for us to get into the space of helping people get past that blank canvas problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:32&lt;/strong&gt; · So I have something I want to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:34&lt;/strong&gt; · I&apos;m not sure where to start.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:35&lt;/strong&gt; · There&apos;s a lot of templates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:37&lt;/strong&gt; · Might as well just throw it into the prompt to see what you can get.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:40&lt;/strong&gt; · And one of the cool things about that is, even if you&apos;re a really experienced dev and maybe mobile is like your wheelhouse, we still offer this prototyper because you could use it for your marketing site.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:53&lt;/strong&gt; · You could use it for the things that you don&apos;t want to build, but then focus your development time on something else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:59&lt;/strong&gt; · And because we run on top of these VMs, unlike a lot of the other tools, we have that experience wherever you go into any browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:09&lt;/strong&gt; · And so I think that&apos;s been fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:11&lt;/strong&gt; · One of the things I like to show off at the booth is running Firebase Studio on my Google Pixel Fold.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:18&lt;/strong&gt; · PETER FRIESE: Yeah, how is the development experience on a small device like that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:21&lt;/strong&gt; · RODY DAVIS: It&apos;s interesting, especially if you have a keyboard and stuff like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:25&lt;/strong&gt; · But, I mean, I think it&apos;s just really awesome to be able to-- oh, I just need to get access to that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:30&lt;/strong&gt; · I just need to make this one change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:32&lt;/strong&gt; · And one of my favorite things that we can do in Firebase Studio is-- everybody likes to joke on Twitter and other places that, oh, here&apos;s my new startup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:43&lt;/strong&gt; · It&apos;s localhost blah blah blah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:46&lt;/strong&gt; · But with Firebase Studio, we launch public ports, so you can actually share localhost on Twitter, which is fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:51&lt;/strong&gt; · PETER FRIESE: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:52&lt;/strong&gt; · Yeah, it&apos;s really good fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:54&lt;/strong&gt; · And what I really like is that you have this feature where you can display QR code and then show it on your phone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:01&lt;/strong&gt; · And that&apos;s pretty cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:03&lt;/strong&gt; · RODY DAVIS: And it&apos;s getting into your actual VM too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:05&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:06&lt;/strong&gt; · PETER FRIESE: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:06&lt;/strong&gt; · Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:06&lt;/strong&gt; · So why don&apos;t we take a look at how it works, and then we can look into some of the features, and maybe you can show us some of the stuff that you&apos;re particularly excited about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:18&lt;/strong&gt; · And then maybe we can take some audience questions as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:22&lt;/strong&gt; · And maybe-- I don&apos;t know-- you have an idea what we can vibe code or the audience wants to come up with something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:29&lt;/strong&gt; · So I will bring on your screen, and then we can take it from there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:35&lt;/strong&gt; · RODY DAVIS: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:35&lt;/strong&gt; · So when you are first brought into Firebase Studio, you won&apos;t have any projects yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:43&lt;/strong&gt; · So this is going to be the screen that you have.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:45&lt;/strong&gt; · It&apos;s very similar to any other of the AI tools, but we offer a way to easily get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:53&lt;/strong&gt; · One of the cool things that we launched recently is when you add a prompt, we can now suggest an improvement to that prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:01&lt;/strong&gt; · So it&apos;ll already start to normalize the type of input that people are bringing, which is pretty cool in itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:10&lt;/strong&gt; · And then, of course, if you want, you can click on Full Dashboard and get access to our templates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:17&lt;/strong&gt; · We have a huge variety of templates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:19&lt;/strong&gt; · There&apos;s almost certainly a framework or a stack that you&apos;re using that we have here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:26&lt;/strong&gt; · We have both mobile, web, and front end.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:30&lt;/strong&gt; · Mobile is especially really popular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:33&lt;/strong&gt; · Our AI templates-- people actually really like our databases inside of Firebase Studio because, notoriously, Postgres and these other ones can be tricky to get installed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:42&lt;/strong&gt; · And so now you can have a running Postgres instance running in the browser that you can connect to by public port.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:49&lt;/strong&gt; · So you can kind of-- what I like to do is create really nice staging environments and then connect to them from other places.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:56&lt;/strong&gt; · And then we also have this new What&apos;s New section that we launched just last week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:01&lt;/strong&gt; · And these are updates to these five templates that support our new agentive AI layout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:09&lt;/strong&gt; · And it kind of makes-- one, we&apos;ve updated the base system instructions, so you can have a more prototyper-esque type of experience, where you can just jump into the chat and say, change this, do this, do whatever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:23&lt;/strong&gt; · PETER FRIESE: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:25&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:25&lt;/strong&gt; · That&apos;s really interesting because when-- that sounds a little bit like on [INAUDIBLE]-- so we&apos;ve got the prototyper that you showed in the beginning, where you have this box that you can use to kickstart your application from zero, essentially.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:40&lt;/strong&gt; · And what you just said sounds a little bit like you would be in the IDE part of the IDE, and then you&apos;d have another window that you could use to continue prototyping in there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:51&lt;/strong&gt; · Is that about correct?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:52&lt;/strong&gt; · RODY DAVIS: Yeah, and we&apos;re also just starting with these.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:55&lt;/strong&gt; · But the goal is to try to bring this to more that makes sense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:57&lt;/strong&gt; · And so if there&apos;s a popular framework, and you&apos;re like, oh, I have to use Laravel for my-- let&apos;s see, where is it-- for my entire application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:08&lt;/strong&gt; · That&apos;s the only thing I use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:10&lt;/strong&gt; · Well, you could take what we&apos;re doing here and apply that to your favorite template and already start to have this more agentive experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:19&lt;/strong&gt; · PETER FRIESE: OK, that&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:20&lt;/strong&gt; · So what I think we should be doing-- so when I was at Cloud Summit in London, I had 15 minutes to vibe code an application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:31&lt;/strong&gt; · And because I know that it takes a while for the prototyper to do its work, I vibe coded two applications at the same time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:39&lt;/strong&gt; · One was a &amp;quot;Flappy Bird&amp;quot; clone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:43&lt;/strong&gt; · And the other one was an expense tracker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:46&lt;/strong&gt; · So it sounds like we might want to do the same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:50&lt;/strong&gt; · So I&apos;m thinking one of the things that we should do is demonstrate how the initial prototyper works, like where you can put your prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:58&lt;/strong&gt; · And then I am super curious about the agentic experience in those templates here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:03&lt;/strong&gt; · So I want to see those two things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:06&lt;/strong&gt; · Can you make that work?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:08&lt;/strong&gt; · RODY DAVIS: Sure, and what template do you prefer we start with?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:11&lt;/strong&gt; · Because this would also be a-- PETER FRIESE: I don&apos;t know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:14&lt;/strong&gt; · Nohe, do you have any preference?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:16&lt;/strong&gt; · ALEXANDER NOHE: Let&apos;s go Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:18&lt;/strong&gt; · RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:18&lt;/strong&gt; · OK, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:19&lt;/strong&gt; · PETER FRIESE: Let&apos;s do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:21&lt;/strong&gt; · Oh, yeah, and then the other app should be a bird-themed app as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:26&lt;/strong&gt; · [LAUGHTER] RODY DAVIS: Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:34&lt;/strong&gt; · And I will have you hide my screen in a second when I paste in an API key.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:40&lt;/strong&gt; · PETER FRIESE: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:41&lt;/strong&gt; · Fair enough.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:47&lt;/strong&gt; · ALEXANDER NOHE: Building environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:48&lt;/strong&gt; · RODY DAVIS: Yeah, no, this is pretty exciting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:51&lt;/strong&gt; · I&apos;ve been really pumped for this new type of experience that we&apos;ve launched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:57&lt;/strong&gt; · One, it kind of just really highlights how flexible and powerful our templates are because the prototyper is great if you&apos;re using Next.js and Tailwind and all the things that we&apos;ve set up for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:11&lt;/strong&gt; · But a lot of people have been asking, I wanted to use my favorite framework, Vue or Svelte.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:16&lt;/strong&gt; · And it&apos;s always giving me Next.js.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:18&lt;/strong&gt; · So this is kind of like a step in that direction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:21&lt;/strong&gt; · PETER FRIESE: Yeah, absolutely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:25&lt;/strong&gt; · RODY DAVIS: So yeah, this is the new template layout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:28&lt;/strong&gt; · As you can see, by default, the chat&apos;s on the right, and the previews are on the left.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:34&lt;/strong&gt; · And then, of course, you still have access to all the code here too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:38&lt;/strong&gt; · And we also launched this new feature recently called Auto-Run, where it essentially-- except for terminal commands, it will automatically add all the files without having to have you approve each one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:49&lt;/strong&gt; · PETER FRIESE: Nice RODY DAVIS: OK, let me go ahead and grab that API key real quick.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:58&lt;/strong&gt; · ALEXANDER NOHE: I think this might be where we want to turn off Rody&apos;s screen?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:00&lt;/strong&gt; · RODY DAVIS: Yeah, go ahead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:01&lt;/strong&gt; · PETER FRIESE: OK, so I&apos;ll target just you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:05&lt;/strong&gt; · RODY DAVIS: Just in case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:09&lt;/strong&gt; · I&apos;m sure there&apos;s eager viewers that are ready to screenshot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:12&lt;/strong&gt; · PETER FRIESE: Oh, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:13&lt;/strong&gt; · So I had one video where I had an API key visible for literally two frames.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:18&lt;/strong&gt; · And then somebody posted a comment in the video that pointed exactly to that thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:24&lt;/strong&gt; · So we had to swap it out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:27&lt;/strong&gt; · RODY DAVIS: Amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:29&lt;/strong&gt; · OK, almost there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:32&lt;/strong&gt; · Sweet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:34&lt;/strong&gt; · Let me just put this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:37&lt;/strong&gt; · ALEXANDER NOHE: So while we wait for Rody to fetch the API key, can we see if he can pat his head and rub his belly at the same time?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:45&lt;/strong&gt; · [LAUGHTER] RODY DAVIS: All right, now we&apos;re good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:49&lt;/strong&gt; · ALEXANDER NOHE: Oh, we&apos;re all good?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:51&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:52&lt;/strong&gt; · PETER FRIESE: [LAUGHS] RODY DAVIS: OK, so one of the cool things when you enter into this Auto-Run is, obviously, it&apos;s very destructive in terms of it&apos;s going to be just adding a bunch of files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:04&lt;/strong&gt; · So we will commit all of your changes to a local Git repo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:07&lt;/strong&gt; · So right here, you can see we generate a commit message.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:11&lt;/strong&gt; · And we do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:12&lt;/strong&gt; · And so if you do a whole session, and you really hate it, you can always right-click and restore.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:19&lt;/strong&gt; · The other thing that you can do is built into the core Code OSS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:24&lt;/strong&gt; · You can also use your timeline and restore there as well, so already kind of nice ways to get around that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:32&lt;/strong&gt; · PETER FRIESE: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:33&lt;/strong&gt; · RODY DAVIS: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:34&lt;/strong&gt; · And let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:35&lt;/strong&gt; · Another fun thing we could do, too, is we recently launched support for Firebase MCP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:42&lt;/strong&gt; · So we can also add that as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:45&lt;/strong&gt; · I can go ahead and log in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:50&lt;/strong&gt; · PETER FRIESE: So that would enable you to have the IDE remotely control your Firebase project and do things like create a project in the first place or create new accounts, list what&apos;s in the database, and stuff like that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:08&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:09&lt;/strong&gt; · Go ahead and hide my screen one more time, too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:11&lt;/strong&gt; · PETER FRIESE: Yep, sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:12&lt;/strong&gt; · RODY DAVIS: Thanks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:15&lt;/strong&gt; · Just realized it&apos;d show the authorization code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:22&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:22&lt;/strong&gt; · ALEXANDER NOHE: So a few questions came in on chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:28&lt;/strong&gt; · And I&apos;m trying to find them so that we can show them on the screen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:31&lt;/strong&gt; · But one of them was, are we planning to make it easy to integrate auth, database, storage, and other Firebase services easily with Studio projects?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:42&lt;/strong&gt; · RODY DAVIS: Yeah, that&apos;s a good question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:44&lt;/strong&gt; · And you can share my screen again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:46&lt;/strong&gt; · The goal is to make all the integrations as seamless as possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:51&lt;/strong&gt; · I think you can already, right now, go to the Firebase Console and set up the authorized domains and some of the other redirects to get that working.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:02&lt;/strong&gt; · One of the cool things about the Firebase MCP server-- and you guys can fact check me if I&apos;m wrong on this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:07&lt;/strong&gt; · But it can already do a lot of things like create Firebase projects and databases and configure a lot of the settings for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:17&lt;/strong&gt; · ALEXANDER NOHE: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:18&lt;/strong&gt; · RODY DAVIS: And then, so one of the cool things is we have this MCP JSON file that is blank.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:24&lt;/strong&gt; · We have a code lab, where I&apos;m just going to paste in the code snippet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:27&lt;/strong&gt; · And this enables the Firebase MCP server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:32&lt;/strong&gt; · And the way that you&apos;ll know it&apos;s running is, if you go into your logs-- ALEXANDER NOHE: Hey, Rody, just to interrupt you, I think it&apos;s about time I say my catchphrase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:43&lt;/strong&gt; · And I know you can read the screen, but I cannot read the screen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:48&lt;/strong&gt; · Could you make it just a little bit bigger?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:50&lt;/strong&gt; · RODY DAVIS: Yeah, you got it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:51&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:57&lt;/strong&gt; · ALEXANDER NOHE: Thank you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:58&lt;/strong&gt; · Yeah, it should be uncomfortably large for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:01&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:03&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:03&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:04&lt;/strong&gt; · So when I went to the Gemini logs here in the output console, you&apos;ll see that it&apos;ll show all the tools that it has available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:13&lt;/strong&gt; · Sometimes it&apos;ll take a second.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:15&lt;/strong&gt; · And then as you make changes to the MCP JSON, it&apos;ll also change what&apos;s available there too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:20&lt;/strong&gt; · Like, for example, Flutter and Dart has an MCP server that you can also use on the beta channel, I believe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:27&lt;/strong&gt; · And so you could have multiple MCPs here, and they&apos;d all be available in chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:32&lt;/strong&gt; · PETER FRIESE: I love the fact that it adds 42 tools, but it also means we can&apos;t add any more tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:38&lt;/strong&gt; · RODY DAVIS: That&apos;s right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:39&lt;/strong&gt; · Yeah, it&apos;s the meaning of life.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:41&lt;/strong&gt; · ALEXANDER NOHE: I did see one tool in there called Consult Assistant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:44&lt;/strong&gt; · Have you tried that tool yet?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:46&lt;/strong&gt; · RODY DAVIS: I have not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:51&lt;/strong&gt; · ALEXANDER NOHE: It&apos;s like third line, maybe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:54&lt;/strong&gt; · Oh, no, no, second line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:55&lt;/strong&gt; · You literally just highlighted it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:58&lt;/strong&gt; · That way, to the right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:59&lt;/strong&gt; · Yeah, that one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:00&lt;/strong&gt; · RODY DAVIS: Yeah, so any idea what we should prompt there to try to get it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:04&lt;/strong&gt; · We can also switch it to Ask mode, by the way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:06&lt;/strong&gt; · This is so you can interact but not make changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:11&lt;/strong&gt; · ALEXANDER NOHE: Can you ask the Firebase Assistant, how do I add FlutterFire to this app?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:26&lt;/strong&gt; · RODY DAVIS: OK, let&apos;s see if it&apos;ll come back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:28&lt;/strong&gt; · I&apos;m also using Gemini 2.5 Pro here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:36&lt;/strong&gt; · Beginning Firebase setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:37&lt;/strong&gt; · It&apos;s just like it&apos;s thinking, mapping out Firebase tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:44&lt;/strong&gt; · ALEXANDER NOHE: And then while we wait for this to respond, somebody mentioned that they are designing a web app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:51&lt;/strong&gt; · And is it always going to be slow?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:55&lt;/strong&gt; · Or when they go live, will it be faster?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:58&lt;/strong&gt; · RODY DAVIS: Slow as in building?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:03&lt;/strong&gt; · ALEXANDER NOHE: This is the text that I have, my friend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:06&lt;/strong&gt; · PETER FRIESE: It runs very slowly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:09&lt;/strong&gt; · RODY DAVIS: It might be debug mode that&apos;s slowing it down for the app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:12&lt;/strong&gt; · ALEXANDER NOHE: Ah, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:17&lt;/strong&gt; · RODY DAVIS: Let&apos;s see if we can-- I&apos;m not sure if it actually used the tool or not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:21&lt;/strong&gt; · Let&apos;s see if we can use the tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:25&lt;/strong&gt; · It did give a correct set of instructions, though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:29&lt;/strong&gt; · ALEXANDER NOHE: How do you know when a tool is run in Ask mode?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:31&lt;/strong&gt; · RODY DAVIS: It should prompt or show a little thing that says, using X tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:38&lt;/strong&gt; · So it also might not work in Ask mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:46&lt;/strong&gt; · Let me try it again in Agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:51&lt;/strong&gt; · And you can always start a new chat, which is nice because you can actually have multiple histories of various chats going.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:57&lt;/strong&gt; · So let&apos;s try it again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:00&lt;/strong&gt; · Yeah, it totally could have just not worked in Ask mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:10&lt;/strong&gt; · Let&apos;s try my terminal command, but yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:11&lt;/strong&gt; · PETER FRIESE: But that&apos;s a little bit more straightforward, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:14&lt;/strong&gt; · RODY DAVIS: Yeah, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:17&lt;/strong&gt; · Just try one more before giving up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:21&lt;/strong&gt; · ALEXANDER NOHE: Maybe like, when should I use Firestore versus Firebase Data Connect?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:29&lt;/strong&gt; · RODY DAVIS: Yeah, I think the issue is it&apos;s trying to just use the raw model stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:43&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:45&lt;/strong&gt; · Here we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:45&lt;/strong&gt; · Clarifying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:46&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:46&lt;/strong&gt; · That&apos;s what you&apos;re supposed to see, though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:48&lt;/strong&gt; · That&apos;s it actually talking to the tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:53&lt;/strong&gt; · No active project was found.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:55&lt;/strong&gt; · So that one, that may need a set for that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:00&lt;/strong&gt; · That&apos;s good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:01&lt;/strong&gt; · I haven&apos;t heard that before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:02&lt;/strong&gt; · PETER FRIESE: Can you ask the MCP server to give you the tool definition so that you can understand what the tool is good for?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:10&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:19&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:28&lt;/strong&gt; · There we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:34&lt;/strong&gt; · Pretty cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:35&lt;/strong&gt; · PETER FRIESE: OK, so the Firebase AI-- Firebase Assistant is basically Gemini in Firebase, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:43&lt;/strong&gt; · RODY DAVIS: Yeah, is that the-- nice, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:46&lt;/strong&gt; · That&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:48&lt;/strong&gt; · PETER FRIESE: Yeah, that&apos;s pretty cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:50&lt;/strong&gt; · RODY DAVIS: Right on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:52&lt;/strong&gt; · PETER FRIESE: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:53&lt;/strong&gt; · So what should we do with this application here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:55&lt;/strong&gt; · So it&apos;s a basic application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:58&lt;/strong&gt; · But can we add a feature?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:03&lt;/strong&gt; · RODY DAVIS: Let&apos;s see if we can first switch to Agent Auto-Run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:09&lt;/strong&gt; · Let&apos;s build a fun-- here we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:13&lt;/strong&gt; · Let&apos;s do, build a-- this is always one I like to try, just because it requires state, logic, and layout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:23&lt;/strong&gt; · And I usually like to do, build me a retro-themed calculator because it usually-- depending on the luck of the draw, it&apos;ll have a nice segmented display with the nice green.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:36&lt;/strong&gt; · PETER FRIESE: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:38&lt;/strong&gt; · ALEXANDER NOHE: Are these solar powered too?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:40&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:40&lt;/strong&gt; · [LAUGHTER] That would be awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:45&lt;/strong&gt; · It&apos;d be cool if you could have the effects of motion on it too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:50&lt;/strong&gt; · ALEXANDER NOHE: Oh, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:56&lt;/strong&gt; · RODY DAVIS: Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:57&lt;/strong&gt; · Yeah, let&apos;s just start with that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:58&lt;/strong&gt; · So I want to start with a prompt that someone might put into the normal prototyper that&apos;s just asking for something, but not really specific on the type of change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:10&lt;/strong&gt; · If you go into our IDX/AI rules, these are the new rules that we launched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:16&lt;/strong&gt; · And so you can see that there&apos;s a lot of opinions in here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:19&lt;/strong&gt; · But that&apos;s the best part about rules is they are opinionated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:22&lt;/strong&gt; · You can go and change these.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:24&lt;/strong&gt; · And it will have your opinions baked into those rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:27&lt;/strong&gt; · PETER FRIESE: Oh, so they&apos;re in your project?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:29&lt;/strong&gt; · RODY DAVIS: Yes, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:30&lt;/strong&gt; · So if you completely hate what we put together, please change it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:35&lt;/strong&gt; · And then just restart the chat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:36&lt;/strong&gt; · And you will have it use your favorite state management, your favorite routing framework, your database, you name it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:42&lt;/strong&gt; · PETER FRIESE: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:43&lt;/strong&gt; · Yeah, good stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:43&lt;/strong&gt; · ALEXANDER NOHE: Could we have it for every text string that it shows prefix it with a Z?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:51&lt;/strong&gt; · RODY DAVIS: I guess.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:52&lt;/strong&gt; · Yeah, you could.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:53&lt;/strong&gt; · I mean, I think, obviously, the longer the rules, the less it may pay attention to any specific rule.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:01&lt;/strong&gt; · ALEXANDER NOHE: Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:02&lt;/strong&gt; · RODY DAVIS: And so-- yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:03&lt;/strong&gt; · So one of the first steps it&apos;s supposed to do is come up with a blueprint and a plan, but we&apos;ll see if it actually does that here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:11&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:12&lt;/strong&gt; · So that was actually a question from someone on the chat who said, quick question, Rody, I noticed the more messages and prompts sent in the chat directly affects the responsiveness of the model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:24&lt;/strong&gt; · Perhaps Firebase Studio can develop a way to clear workspace data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:28&lt;/strong&gt; · RODY DAVIS: So you can do slash clear to remove all the chats in a session.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:34&lt;/strong&gt; · And that&apos;s a way to really speed it up already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:38&lt;/strong&gt; · And then there&apos;s some other techniques we can do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:41&lt;/strong&gt; · Like, if it&apos;s really slow, you can go to your workspace list and click Restart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:45&lt;/strong&gt; · And that will provision a new one for you as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:48&lt;/strong&gt; · PETER FRIESE: Is there also a way to summarize the previous conversation and roll it down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:54&lt;/strong&gt; · RODY DAVIS: You can with Gemini CLI, which we have pre-installed now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:58&lt;/strong&gt; · You can just go to terminal and type Gemini and have all those features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:02&lt;/strong&gt; · PETER FRIESE: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:03&lt;/strong&gt; · RODY DAVIS: Also, here&apos;s the blueprint that it generated, which is cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:05&lt;/strong&gt; · It&apos;s similar to the prototyper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:07&lt;/strong&gt; · Like, it describes the UI, the design features, and the structure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:11&lt;/strong&gt; · And let&apos;s do a restart to make sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:17&lt;/strong&gt; · It did not set it up in the-- let me do a full restart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:23&lt;/strong&gt; · PETER FRIESE: Maybe it&apos;s on the Plus button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:28&lt;/strong&gt; · RODY DAVIS: I think it needs to be fully reloaded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:30&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:34&lt;/strong&gt; · ALEXANDER NOHE: Yeah, hot reload is not-- no, that did just come out to the web, right, because I saw something-- RODY DAVIS: For Flutter, it came out for Chrome, but not web server, which is what we use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:44&lt;/strong&gt; · ALEXANDER NOHE: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:45&lt;/strong&gt; · RODY DAVIS: Yeah, there&apos;s plans to support it in the future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:46&lt;/strong&gt; · ALEXANDER NOHE: Can we get to the Android preview?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:48&lt;/strong&gt; · RODY DAVIS: Yeah, it&apos;s also restarting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:50&lt;/strong&gt; · ALEXANDER NOHE: Ah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:52&lt;/strong&gt; · RODY DAVIS: It just installed Google Fonts, so it&apos;s probably installing that package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:56&lt;/strong&gt; · ALEXANDER NOHE: While we wait for a hot reload, there was a question about the Attachment button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:02&lt;/strong&gt; · And clicking on the Attachment button-- ooh, look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:06&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:07&lt;/strong&gt; · ALEXANDER NOHE: It allows you to upload images, but not-- RODY DAVIS: Yeah, you can-- ALEXANDER NOHE: --not files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:12&lt;/strong&gt; · RODY DAVIS: --upload drawings, images, or even files from the workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:17&lt;/strong&gt; · ALEXANDER NOHE: Sorry, it looks like the question was in the overview in the base window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:25&lt;/strong&gt; · Do you know what I&apos;m talking about, where you launch Studio, the prompting?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:28&lt;/strong&gt; · RODY DAVIS: Oh, that one only supports images, I believe, or only-- yeah, only images, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:34&lt;/strong&gt; · ALEXANDER NOHE: Yeah, so I think there&apos;s a feature request to support markdown there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:39&lt;/strong&gt; · RODY DAVIS: Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:40&lt;/strong&gt; · I also have that feature request out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:42&lt;/strong&gt; · PETER FRIESE: Oh, to input markdown right in the initial prompt?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:46&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:47&lt;/strong&gt; · I will say that we support-- if you go to the open in Firebase Studio docs for a button, it supports sending stuff to the prototyper, which I have not found a limit to the amount of text you can provide there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:01&lt;/strong&gt; · So I have submitted full docs created with Gemini Deep Research to there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:05&lt;/strong&gt; · And it&apos;ll just-- like, you can add it as context inside of there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:08&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:09&lt;/strong&gt; · What I did found is that once you have the blueprint, you can say, let me edit the blueprint, and that&apos;s a markdown file as well, and then do whatever you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:19&lt;/strong&gt; · But yeah, obviously, you&apos;d want to also start out from a markdown file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:24&lt;/strong&gt; · RODY DAVIS: I was just about to code like a normal person.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:27&lt;/strong&gt; · So let me use AI to make that [INAUDIBLE].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:30&lt;/strong&gt; · PETER FRIESE: Like a savage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:31&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:33&lt;/strong&gt; · Remove the debug banner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:37&lt;/strong&gt; · So, as you can see, it came with a pretty cool UI already with a nice retro font and already with some animations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:45&lt;/strong&gt; · And that kind of shows you that we just gave it-- it generated a retro-themed themed calculator, but it did the work of creating a plan.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:54&lt;/strong&gt; · The nice thing about the blueprint is it really makes an elaborate expansion on top of your prompt, and then also your intention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:02&lt;/strong&gt; · It&apos;s almost like a PRD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:03&lt;/strong&gt; · And then we generated all those files at once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:08&lt;/strong&gt; · So that was 1, 2, 3-- three files with a pubspec change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:12&lt;/strong&gt; · And this is something that&apos;s in the rules, too, is instead of updating the pubspec manually, it uses the CLI, which we prompted it to, so it always knows the correct way to do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:23&lt;/strong&gt; · So for our Angular template, when you add a new component, it uses the Angular CLI to generate the new components and routes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:30&lt;/strong&gt; · So I think doing it this way lets the model do what it&apos;s best at, and then, also, what the tools are do what they&apos;re best at as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:42&lt;/strong&gt; · ALEXANDER NOHE: So one thing that I&apos;ve noticed using Gemini CLI is that when I have a really targeted fix, it tends to be really good at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:55&lt;/strong&gt; · Whereas, when I vibe code with Firebase Studio, sometimes I want to have a targeted fix.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:59&lt;/strong&gt; · I&apos;m like, change this thing to do this, or the math is not exactly mathing correctly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:05&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:06&lt;/strong&gt; · ALEXANDER NOHE: Is there a way to go-- ooh, is there a way to figure out where in my project this button is?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:14&lt;/strong&gt; · Like the 9 button, is this a single button on a screen that&apos;s reused for all the number buttons?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:19&lt;/strong&gt; · Because all the operations are one shade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:21&lt;/strong&gt; · All the number buttons are another shade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:22&lt;/strong&gt; · Is there a way for me to figure that out by prompting Gemini in Studio, or is there something, maybe, else going on?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:29&lt;/strong&gt; · RODY DAVIS: So I think one of the ways-- like, that&apos;s what you would use the Ask mode for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:34&lt;/strong&gt; · You could ask those type of questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:37&lt;/strong&gt; · And then we&apos;ve indexed all the files, and so it would know which file is where, and then, also, the context and state dynamically of what you&apos;ve just added and updated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:48&lt;/strong&gt; · Is that similar to what you&apos;re asking?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:51&lt;/strong&gt; · ALEXANDER NOHE: Maybe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:53&lt;/strong&gt; · I was working on something recently where I knew that I wanted it to have a certain behavior.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:00&lt;/strong&gt; · And rather than just ask Gemini for the entire app, I said, in this file, change this thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:06&lt;/strong&gt; · Is there a way to figure out from this which file that would be so that I could take a look myself?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:13&lt;/strong&gt; · Do you know what I&apos;m trying to ask?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:14&lt;/strong&gt; · RODY DAVIS: I think so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:16&lt;/strong&gt; · I think you&apos;re asking-- so I guess the way I would do that is if I know the file ahead of time, I usually tell Gemini directly in the chat, update or read this file, describe this file to me, or, what do I need to do to make this file better?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:33&lt;/strong&gt; · Something like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:33&lt;/strong&gt; · And then I&apos;m having a conversation with that file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:37&lt;/strong&gt; · With the file from the workspace, you can even add it there too if it&apos;s like a deeply nested one too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:43&lt;/strong&gt; · ALEXANDER NOHE: What if I don&apos;t know the file?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:45&lt;/strong&gt; · RODY DAVIS: So I would actually just start by asking, because Gemini CLI is really good at this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:51&lt;/strong&gt; · And even in this mode too, we have an index list of all the stuff in your workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:55&lt;/strong&gt; · So you can say, I&apos;m trying to find out where this is in the code base.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:01&lt;/strong&gt; · And it could search stuff like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:04&lt;/strong&gt; · ALEXANDER NOHE: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:05&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:06&lt;/strong&gt; · Because we&apos;ve actually sent the list of the files and the recent stuff and everything like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:11&lt;/strong&gt; · Gemini CLI will actually run regex queries and other stuff too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:16&lt;/strong&gt; · ALEXANDER NOHE: And figure out exactly-- RODY DAVIS: Mm-hmm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:19&lt;/strong&gt; · ALEXANDER NOHE: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:20&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:22&lt;/strong&gt; · ALEXANDER NOHE: Neat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:23&lt;/strong&gt; · RODY DAVIS: So there&apos;s different goals and different trade-offs, for sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:27&lt;/strong&gt; · But yeah, I think sometimes you&apos;re like, this is a new code base.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:31&lt;/strong&gt; · I&apos;m explorative, trying to figure out where everything is and how it&apos;s working.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:37&lt;/strong&gt; · One of the things I&apos;ve done usually, either with CLI or regular, is just try to generate markdown files of what it thinks the app architecture is and try to have it go through and scan and create state and build it up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:53&lt;/strong&gt; · Or even like for these files, generate a series of tests, then run them, because then that kind of also generates a contract of what it thinks that the correct output should be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:05&lt;/strong&gt; · And then, as I&apos;m going too, I like to have it generate documentation comments in the code too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:12&lt;/strong&gt; · So it&apos;s kind of just leaving it better as it goes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:16&lt;/strong&gt; · ALEXANDER NOHE: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:17&lt;/strong&gt; · Yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:19&lt;/strong&gt; · RODY DAVIS: Because also, it&apos;s natural language text, so it&apos;s great for context when it loads in the file too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:25&lt;/strong&gt; · ALEXANDER NOHE: Speaking of context, is there a limit to the number of tokens we can use in Firebase Studio?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:32&lt;/strong&gt; · RODY DAVIS: So we&apos;re using Gemini 2.5 Pro here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:34&lt;/strong&gt; · We switched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:35&lt;/strong&gt; · So Gemini supports up to a million.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:40&lt;/strong&gt; · Obviously, it&apos;s less than that with the context that we send in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:44&lt;/strong&gt; · But I think context is one thing, but usable context is different, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:50&lt;/strong&gt; · When you&apos;re working with code, I think it&apos;s a lot different than just chatting with a PDF.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:55&lt;/strong&gt; · And the more that you give it, the harder it is to find the exact things that you&apos;re looking for and to have meaningful change output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:04&lt;/strong&gt; · I&apos;ve noticed that my best performance is when I&apos;m-- even in CLI, I&apos;m hitting like 75%, 80% context window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:13&lt;/strong&gt; · I feel it&apos;s like the sweet spot for-- I have a lot of context in there, but then I still have a lot of performance, because the performance will go down the more you fill it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:24&lt;/strong&gt; · Even with caching, it still takes a while to load it in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:28&lt;/strong&gt; · It still takes a while to parse and run inference on that too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:33&lt;/strong&gt; · So it&apos;s just definitely a trade-off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:36&lt;/strong&gt; · But there are times when you want to fill it up and do Ask questions like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:43&lt;/strong&gt; · ALEXANDER NOHE: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:47&lt;/strong&gt; · Peter looks like he&apos;s dying to ask a question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:49&lt;/strong&gt; · PETER FRIESE: [LAUGHS] Yeah, so you mentioned that you can ask it to generate documentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:01&lt;/strong&gt; · So why don&apos;t we ask it to generate a diagram of this calculator for us?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:06&lt;/strong&gt; · RODY DAVIS: Yeah, let&apos;s do it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:07&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:10&lt;/strong&gt; · Let&apos;s do, in the blueprint.md, generate a diagram of the application features and state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:28&lt;/strong&gt; · Let&apos;s see if we can do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:34&lt;/strong&gt; · So we can see it reads, and then we&apos;ll make the changes, and then also apply the edit out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:40&lt;/strong&gt; · We did switch to Auto-Run here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:43&lt;/strong&gt; · Doing a nice mermaid diagram-- nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:47&lt;/strong&gt; · So let&apos;s go ahead and-- hey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:53&lt;/strong&gt; · PETER FRIESE: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:54&lt;/strong&gt; · ALEXANDER NOHE: Ooh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:57&lt;/strong&gt; · Wow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:57&lt;/strong&gt; · PETER FRIESE: Can we preview the diagram somehow?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:00&lt;/strong&gt; · RODY DAVIS: Preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:03&lt;/strong&gt; · ALEXANDER NOHE: I think you want markdown preview, which might be an extension.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:07&lt;/strong&gt; · Or if you do Command-Shift-P too, open up the command palette.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:10&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:11&lt;/strong&gt; · I think we-- ALEXANDER NOHE: Would that work?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:12&lt;/strong&gt; · No.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:12&lt;/strong&gt; · RODY DAVIS: I wonder if we render it in our-- I don&apos;t know if the built-in-- PETER FRIESE: Oh, can you go-- so I see the button, so right next to the diamond icon, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:21&lt;/strong&gt; · RODY DAVIS: Yeah, that&apos;s the one I just did, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:23&lt;/strong&gt; · I don&apos;t think we render mermaid diagrams in here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:27&lt;/strong&gt; · PETER FRIESE: Oh, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:28&lt;/strong&gt; · ALEXANDER NOHE: Not yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:29&lt;/strong&gt; · RODY DAVIS: Yeah, not yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:32&lt;/strong&gt; · PETER FRIESE: OK, feature request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:34&lt;/strong&gt; · ALEXANDER NOHE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:34&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:35&lt;/strong&gt; · I&apos;m trying to figure out-- I mean, I know GitHub will render it and other places, but yeah, I mean, at least it rendered the diagram, which is cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:46&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:47&lt;/strong&gt; · It has the list of operations, the different states, subgraph.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:55&lt;/strong&gt; · Yeah, this is a calculator in state diagram.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:58&lt;/strong&gt; · That&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:00&lt;/strong&gt; · Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:02&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:03&lt;/strong&gt; · And then the other thing too, we could take this further and be like, OK, great, update the code you generated to have documentation comments, and write some unit and widget tests, and run the test, checking for errors and fixing any issues.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:45&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:46&lt;/strong&gt; · And then we&apos;ll do, create plan and get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:54&lt;/strong&gt; · See if we can try this out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:55&lt;/strong&gt; · So this is a relatively complex question with a series of compound things that I&apos;m wanting it to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:03&lt;/strong&gt; · So obviously, I want it to do a chain of thought first and then-- all right, we&apos;ll go ahead, and it looks like it&apos;s refactoring it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:13&lt;/strong&gt; · It&apos;s moving things around to a more layered architecture, which is cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:22&lt;/strong&gt; · Oh, it looks like the calculator is responsive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:24&lt;/strong&gt; · Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:25&lt;/strong&gt; · ALEXANDER NOHE: Well, you can&apos;t see the results at the top, though, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:31&lt;/strong&gt; · RODY DAVIS: Yeah, I mean, if you don&apos;t show the answer, it could be correct, so TBD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:36&lt;/strong&gt; · ALEXANDER NOHE: We just hope.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:38&lt;/strong&gt; · [LAUGHTER] RODY DAVIS: Go ahead and move this down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:45&lt;/strong&gt; · If you add the results but never look at it, is it correct?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:48&lt;/strong&gt; · Is it a superposition of what you&apos;ve added?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:54&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:55&lt;/strong&gt; · So it generated the test.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:56&lt;/strong&gt; · Let&apos;s go ahead and run them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:57&lt;/strong&gt; · It&apos;s probably going to fail on the default one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:00&lt;/strong&gt; · I think we&apos;ve already removed it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:01&lt;/strong&gt; · We&apos;ll see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:03&lt;/strong&gt; · But one of the nice things about having Gemini run the commands here is it will return all the standard error and standard out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:12&lt;/strong&gt; · So it can fix screens and fix any issues.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:20&lt;/strong&gt; · Yeah, it looks like there&apos;s some errors that are going to return.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:26&lt;/strong&gt; · ALEXANDER NOHE: So while this is running, we have a few more questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:30&lt;/strong&gt; · RODY DAVIS: Sounds good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:32&lt;/strong&gt; · ALEXANDER NOHE: It&apos;s hard to keep up with all of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:34&lt;/strong&gt; · So why isn&apos;t Android-native development not supported by Firebase Studio yet?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:39&lt;/strong&gt; · RODY DAVIS: So we do support it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:40&lt;/strong&gt; · It&apos;s on pause right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:41&lt;/strong&gt; · But we do have Android Studio Cloud, where you can go onto the mobile one and launch an Android Studio workspace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:50&lt;/strong&gt; · ALEXANDER NOHE: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:52&lt;/strong&gt; · Chrome extensions-- can we use Studio to build those?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:56&lt;/strong&gt; · RODY DAVIS: Yeah, I mean, you can.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:58&lt;/strong&gt; · I think, Nohe, you&apos;ve explored this a little bit too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:00&lt;/strong&gt; · I think you can build the extension and download and pull out the bundle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:05&lt;/strong&gt; · I mean, I don&apos;t think you could run a debug version of the Chrome extension because it needs the Chrome APIs, and we&apos;re running in an iframe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:13&lt;/strong&gt; · But you can literally do whatever you&apos;re going to do inside of Code OSS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:18&lt;/strong&gt; · ALEXANDER NOHE: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:19&lt;/strong&gt; · And then, what&apos;s this going to cost me, Rody?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:24&lt;/strong&gt; · RODY DAVIS: Yeah, so we have our pricing page, but everybody gets three free workspaces and then one free Flutter and React Native space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:33&lt;/strong&gt; · And then you can join the GDP program to get more workspaces.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:38&lt;/strong&gt; · ALEXANDER NOHE: GDP-- Gross Domestic Product program?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:41&lt;/strong&gt; · RODY DAVIS: That is correct-- no, Google Developer Program.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:44&lt;/strong&gt; · ALEXANDER NOHE: Oh, OK, gotcha.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:45&lt;/strong&gt; · I get those mixed up all the time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:47&lt;/strong&gt; · And then what about tagging or editing individual files?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:51&lt;/strong&gt; · Why can&apos;t we?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:52&lt;/strong&gt; · RODY DAVIS: You can.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:53&lt;/strong&gt; · You can just reference it by name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:56&lt;/strong&gt; · We have an index of the entire file list that you have.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:00&lt;/strong&gt; · Like as you saw earlier, I just said, update blueprint.md, and it knew because of the ones in my file list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:11&lt;/strong&gt; · In Gemini CLI, you can tag it with an at symbol.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:14&lt;/strong&gt; · And we have Gemini CLI right here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:15&lt;/strong&gt; · ALEXANDER NOHE: You can also do that here, right, in the Attachment button, if you go down there?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:20&lt;/strong&gt; · RODY DAVIS: So yeah, you can attach it, but it would load it as a context file as opposed to a file path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:28&lt;/strong&gt; · So it&apos;s less prescriptive on, I want you to update this file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:33&lt;/strong&gt; · It&apos;s more like, here&apos;s a file its context is in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:36&lt;/strong&gt; · PETER FRIESE: So I think one of the advantages of being able to tag a file is that you can be sure that you&apos;re talking about the right file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:43&lt;/strong&gt; · Specifically, if you are in a project that has a large folder structure, and you might have a list of those files with the same name, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:52&lt;/strong&gt; · RODY DAVIS: I literally just go copy relative path, and I paste it in, and just do it that way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:56&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:57&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:58&lt;/strong&gt; · So while that works, I think this sounds like a feature request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:02&lt;/strong&gt; · This is something that the team is looking into or people should be going on UserVoice to request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:11&lt;/strong&gt; · RODY DAVIS: Yeah, or you can use Gemini CLI right now and tag those by name as well too, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:16&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:18&lt;/strong&gt; · So if I do something with the Gemini CLI, will it take the blueprint into consideration?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:26&lt;/strong&gt; · RODY DAVIS: Yep, because it also has context of the same folder structure that you&apos;ve launched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:32&lt;/strong&gt; · And so one of the things we&apos;ve done is make sure that these rules files work for both.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:37&lt;/strong&gt; · Like, we generated a Gemini.md for the same thing that we have for the AI rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:41&lt;/strong&gt; · So it&apos;s going to have the same context there for this type of prototyping experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:46&lt;/strong&gt; · PETER FRIESE: So the only difference would be that if I have a chat conversation going in the prototyping chat UI, and then I go into the CLI, it wouldn&apos;t have the chat context, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:59&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:00&lt;/strong&gt; · PETER FRIESE: Except if I asked, can you please summarize this conversation for me?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:03&lt;/strong&gt; · And then typed it into a CLI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:06&lt;/strong&gt; · RODY DAVIS: Or write it to a markdown file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:08&lt;/strong&gt; · I think that&apos;s the blueprint-- a good way to do that, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:10&lt;/strong&gt; · PETER FRIESE: Yeah, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:13&lt;/strong&gt; · ALEXANDER NOHE: Can we actually look at the test that it wrote?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:16&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:17&lt;/strong&gt; · And yeah, it went through the tests, caught errors, and even fixed the other ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:22&lt;/strong&gt; · So let&apos;s go in here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:23&lt;/strong&gt; · ALEXANDER NOHE: So whenever I fail a test, I usually just delete the test and move on with my life.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:28&lt;/strong&gt; · RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:29&lt;/strong&gt; · It did delete the default widget test.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:31&lt;/strong&gt; · That was for the Hello World project that it generates the counter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:36&lt;/strong&gt; · ALEXANDER NOHE: Oh, how&apos;d it fail that one?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:40&lt;/strong&gt; · RODY DAVIS: Because when we-- yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:42&lt;/strong&gt; · Oh, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:43&lt;/strong&gt; · ALEXANDER NOHE: No, I&apos;m making a joke.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:44&lt;/strong&gt; · RODY DAVIS: So what&apos;s cool is it generated a unit test like I asked it to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:50&lt;/strong&gt; · So that was the domain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:52&lt;/strong&gt; · And then it also generated a widget test, which is for the UI testing, so that&apos;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:58&lt;/strong&gt; · ALEXANDER NOHE: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:01&lt;/strong&gt; · RODY DAVIS: Which I always forget the correct way to write widget test.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:04&lt;/strong&gt; · So it&apos;s always great that Gemini can write these as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:09&lt;/strong&gt; · ALEXANDER NOHE: I used to write a lot of test files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:11&lt;/strong&gt; · Like, how creative at doing the wrong thing can it get with test files, do you think?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:17&lt;/strong&gt; · RODY DAVIS: I think-- ALEXANDER NOHE: Oh, hold on, there&apos;s a division by 0 there, right, towards the bottom?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:24&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:25&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:26&lt;/strong&gt; · Division by 0, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:28&lt;/strong&gt; · ALEXANDER NOHE: Oh, look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:30&lt;/strong&gt; · RODY DAVIS: I&apos;ve actually found with Gemini 2.5 Pro, natural language to unit test is actually pretty great if you know the-- well, one, it can just kind of generate whatever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:41&lt;/strong&gt; · But if you have a very guided series of things you want to test, what I usually do is I provide it a couple pseudocode examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:48&lt;/strong&gt; · And then it will stamp out and continue the pattern.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:52&lt;/strong&gt; · I&apos;ve had pretty good luck with that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:54&lt;/strong&gt; · ALEXANDER NOHE: So Rody, I want to ask you about your intentions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:59&lt;/strong&gt; · Is it intentional that Firebase Studio don&apos;t handle the database provisioning like Firestore?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:04&lt;/strong&gt; · In most docs, it&apos;s just a prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:06&lt;/strong&gt; · But one has to go to Firestore to provision it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:08&lt;/strong&gt; · Otherwise, it fails.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:09&lt;/strong&gt; · Can you talk about that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:13&lt;/strong&gt; · RODY DAVIS: Doesn&apos;t the Firebase MCP server now support generating Firestore?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:19&lt;/strong&gt; · ALEXANDER NOHE: I believe you might be right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:21&lt;/strong&gt; · RODY DAVIS: It might be one of the new tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:23&lt;/strong&gt; · So one of the things that the Firebase MCP that we&apos;re investing in is the fact that it can generate a lot of these things from the console that you would have had to do otherwise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:36&lt;/strong&gt; · So ideally, the Firebase MCP just gets ultra powerful over time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:42&lt;/strong&gt; · And then we&apos;re just able to integrate with that and then use that as like a connection point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:48&lt;/strong&gt; · ALEXANDER NOHE: So I was definitely paying attention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:50&lt;/strong&gt; · But for Peter, who wasn&apos;t, does the Firebase MCP come already set up out of the box when you launch Studio?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:57&lt;/strong&gt; · RODY DAVIS: It does not, but you can follow our codelab or any of the other docs on how to-- it&apos;s just literally a one-line copy into this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:06&lt;/strong&gt; · Like, you just paste in this one section, and then it installs the Firebase tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:11&lt;/strong&gt; · The Firebase tools are pre-installed on the machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:13&lt;/strong&gt; · But this just allows you to connect it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:16&lt;/strong&gt; · And you can also do dot Gemini slash MCP, if you want, in the Gemini CLI as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:24&lt;/strong&gt; · ALEXANDER NOHE: Very cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:25&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:30&lt;/strong&gt; · Yeah, so let&apos;s have it to do another change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:32&lt;/strong&gt; · Let&apos;s do, make the calculator have a nice color gradient with animated borders.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:49&lt;/strong&gt; · This is a more complex UI thing, but it might be cool to see what it comes back with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:55&lt;/strong&gt; · I found the iteration to be really nice to be able to try out different ideas, especially knowing that it&apos;s going to be inside of a Git repo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:05&lt;/strong&gt; · I can just restore it anytime.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:08&lt;/strong&gt; · ALEXANDER NOHE: How do you go about restoring it if you are running something, and you&apos;re like, oh, gosh, that-- RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:14&lt;/strong&gt; · ALEXANDER NOHE: Oh, right there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:15&lt;/strong&gt; · RODY DAVIS: So you can literally just go in here, and you can-- I believe it&apos;s-- yeah, Checkout or Cherry Pick.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:21&lt;/strong&gt; · And you can also even do revert changes, I believe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:25&lt;/strong&gt; · And yeah, if you have a staged one, you can always discard all those as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:29&lt;/strong&gt; · But like I said, VS Code also has this feature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:32&lt;/strong&gt; · I don&apos;t know why it&apos;s not showing up, but the timeline also will usually show changes as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:38&lt;/strong&gt; · ALEXANDER NOHE: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:40&lt;/strong&gt; · RODY DAVIS: Let&apos;s do-- but yeah, it&apos;s already like we&apos;re trying to bring the best of what people love about the prototyper into Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:56&lt;/strong&gt; · And I think this kind of makes it a nice way to start with whatever framework.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:03&lt;/strong&gt; · I think we started with web and React and Next.js and Angular and Flutter because those are the most visual ones that we had.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:12&lt;/strong&gt; · But there&apos;s nothing stopping you from setting up the Go template or setting up, like I said, Vue or Svelte or something like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:20&lt;/strong&gt; · So we actually encourage the community to try it out and share their own rules files and their own MCP server setups and stuff like that, because I also played around with Astro and getting this working too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:33&lt;/strong&gt; · And it&apos;s kind of cool to just vibe code a content-generated site.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:38&lt;/strong&gt; · So yeah, it&apos;s fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:40&lt;/strong&gt; · PETER FRIESE: So does that mean if I have any kind of workspace-- so let&apos;s say I have a project that has been sitting around somewhere in GitHub.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:52&lt;/strong&gt; · And it uses some sort of technology stack that might not even be supported.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:59&lt;/strong&gt; · Can I import it into Firebase Studio and then use the prototyper to start working on my project again?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:10&lt;/strong&gt; · RODY DAVIS: So you can follow the files here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:13&lt;/strong&gt; · You just have to, in VS Code settings, make sure that agentic workspace is true, and make sure that you have a rules file there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:23&lt;/strong&gt; · You can copy it and customize it to whatever your project is specific.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:28&lt;/strong&gt; · But then you just literally go into import repo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:30&lt;/strong&gt; · And as long as those files are there-- oh, and then dev.nix.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:34&lt;/strong&gt; · You would definitely need that if you don&apos;t have one already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:37&lt;/strong&gt; · But as long as-- ALEXANDER NOHE: Like, Peter&apos;s looking for Vapor and Swift, if I had to guess.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:43&lt;/strong&gt; · RODY DAVIS: Nice, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:44&lt;/strong&gt; · You could use server-side Swift here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:45&lt;/strong&gt; · Yeah, for sure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:46&lt;/strong&gt; · And you could run it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:48&lt;/strong&gt; · PETER FRIESE: Yeah, I was looking into that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:50&lt;/strong&gt; · And I hit a wall because I wanted to use the latest version of Swift, Swift 6.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:56&lt;/strong&gt; · And apparently, that is not supported on Nix.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:59&lt;/strong&gt; · Somebody seems to be working on that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:01&lt;/strong&gt; · RODY DAVIS: Yeah, you can do unstable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:04&lt;/strong&gt; · So if something isn&apos;t supported, you can also, in the packages, you can-- there&apos;s a way in Nix to actually just install the binary directly from a GitHub release.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:16&lt;/strong&gt; · And so, with that SHA, you could literally install the binary and do all the different kind of stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:23&lt;/strong&gt; · So there&apos;s definitely escape hatches for that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:25&lt;/strong&gt; · But yeah, I agree that sometimes if there&apos;s a version that&apos;s just slightly too new or just whatever, it can be annoying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:32&lt;/strong&gt; · But one of the whole core tenets of Nix is all of the packages get updated together with the channel, so everything&apos;s guaranteed to work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:41&lt;/strong&gt; · And so all 120,000 packages will compile because of the way that they do like their monorepo and stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:51&lt;/strong&gt; · PETER FRIESE: Interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:52&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:53&lt;/strong&gt; · But coming back to my original question, so if I add those three lines to my pre-existing repository, I could somehow make that agentive enabled?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:07&lt;/strong&gt; · RODY DAVIS: Yep, exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:08&lt;/strong&gt; · PETER FRIESE: Is that correct?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:08&lt;/strong&gt; · RODY DAVIS: Yep.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:09&lt;/strong&gt; · Mm-hmm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:10&lt;/strong&gt; · PETER FRIESE: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:11&lt;/strong&gt; · RODY DAVIS: Looks like it generated a nice animated calculator that&apos;s showing up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:18&lt;/strong&gt; · PETER FRIESE: We should make the color change in response to the value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:23&lt;/strong&gt; · RODY DAVIS: Yeah, let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:25&lt;/strong&gt; · To the numbers?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:26&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:27&lt;/strong&gt; · ALEXANDER NOHE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:28&lt;/strong&gt; · PETER FRIESE: In response to the result. The higher the result, the hotter the number, the hotter the color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:34&lt;/strong&gt; · ALEXANDER NOHE: More neon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:36&lt;/strong&gt; · RODY DAVIS: Like real-life conditional formatting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:37&lt;/strong&gt; · PETER FRIESE: [LAUGHS] Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:51&lt;/strong&gt; · RODY DAVIS: Yeah, this will be cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:54&lt;/strong&gt; · Yeah, it&apos;s also just kind of fun to-- like, you could build this in a period of time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:02&lt;/strong&gt; · But while it was generating, we were having discussions about other stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:06&lt;/strong&gt; · It lets you focus on the things that you as a developer enjoy, which is thinking about the abstract of the application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:13&lt;/strong&gt; · And also, at any point, you just can go into the code and do it a different way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:19&lt;/strong&gt; · There&apos;s many times where Gemini will take it a wrong direction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:23&lt;/strong&gt; · And I&apos;m like, OK, let me give it a better stub to start from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:26&lt;/strong&gt; · And then from that stub, you will have a way more high-quality result because AI generation&apos;s kind of like image generation, where if you try to generate an image with five words, it&apos;s going to not be what you probably had in mind for what you&apos;re trying to generate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:45&lt;/strong&gt; · And so if you think about project and code generation the same way, be very descriptive, or at least like, this is the things that I want you to do, avoid the things that I don&apos;t want you to do, but also just kind of guiding it and growing it into the direction you&apos;re wanting as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:02&lt;/strong&gt; · PETER FRIESE: Yeah, that was a question around an aspect of vibe coding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:10&lt;/strong&gt; · You have the tool generate code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:13&lt;/strong&gt; · And then, even if you are a seasoned developer, and you might be an expert in the framework that it&apos;s using, sometimes it&apos;s hard to spot mistakes in the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:23&lt;/strong&gt; · So here, a user asked, if the AI makes a mistake in the code that&apos;s hard to spot, like a hidden bug or security problem, how does the tool help me find and fix it, even if I don&apos;t notice right away?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:37&lt;/strong&gt; · RODY DAVIS: So one of the things about being a developer is you should view AI-generated code as untrusted legacy code too because it is code that you didn&apos;t write or code that is immediately stale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:51&lt;/strong&gt; · And what do you do with untrusted code?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:54&lt;/strong&gt; · You write unit tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:56&lt;/strong&gt; · You run fuzzing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:59&lt;/strong&gt; · You do pen testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:00&lt;/strong&gt; · All these approaches still apply to vibe coding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:03&lt;/strong&gt; · So that&apos;s why I had it generate a series of tests, like you said, Peter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:08&lt;/strong&gt; · It even tested for-- or Nohe, it divided by 0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:11&lt;/strong&gt; · Like, you want to cover all of your cases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:15&lt;/strong&gt; · And the more that you can generate unit tests and widget tests of being-- even if it&apos;s three more times tests than it is actual code, or even simulation testing, or anything, you want to get to a point where you&apos;re covering at least a sufficient amount of the code paths.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:33&lt;/strong&gt; · And you can get closer by-- different tools used, like code coverage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:38&lt;/strong&gt; · And I&apos;ve been like, OK, run code coverage, check which files still need coverage, try to write new unit tests, and go through a loop and have it try to check for all the branches because, usually, you&apos;ll uncover different errors there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:53&lt;/strong&gt; · I think the less tests and the less reviewing that you&apos;ve done in a code base, the more likely it is to have a subtle off-by-one error or a subtle security issue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:04&lt;/strong&gt; · But we also have tools that will help you as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:06&lt;/strong&gt; · We have Firebase App Check, which will make sure that the correct users are there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:10&lt;/strong&gt; · We have Firebase Auth, which will make sure that your application is protected and locked down to only authorized users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:18&lt;/strong&gt; · We have ways to do Firebase security rules to make sure that only the correct data is done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:23&lt;/strong&gt; · So even outside of your application, you can still sandbox it to a certain degree.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:27&lt;/strong&gt; · But yeah, at the end of the day, you still need to write tests and have it generate the types of things that you would do even if you were another developer on the team reviewing code from someone else that&apos;s working on some new feature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:41&lt;/strong&gt; · ALEXANDER NOHE: Are you saying I can&apos;t just say, hey, there&apos;s a bug in here, go find it, even if I don&apos;t know whether or not there&apos;s a bug in there?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:48&lt;/strong&gt; · RODY DAVIS: You can, but it might generate a new bug while it fixes the old one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:52&lt;/strong&gt; · One of the things about programming is, oftentimes, a fix for one thing will uncover two different things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:00&lt;/strong&gt; · ALEXANDER NOHE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:01&lt;/strong&gt; · And then you have that load-bearing bug that&apos;s rooted into the application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:05&lt;/strong&gt; · Everybody&apos;s like, oh, no, you just have to save, close, save, close, and then you can add a new layer in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:10&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:10&lt;/strong&gt; · RODY DAVIS: It becomes a feature at that point, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:12&lt;/strong&gt; · ALEXANDER NOHE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:13&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:14&lt;/strong&gt; · So adding on to my previous question, here is a user who said they have been using Firebase Studio successfully as someone with zero knowledge about coding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:27&lt;/strong&gt; · And they&apos;re struggling to get their app into the Google Play store.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:31&lt;/strong&gt; · So let&apos;s talk about that part of the question next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:35&lt;/strong&gt; · But first, coming back to what you said, Rody, which I totally agree with, you have to review the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:45&lt;/strong&gt; · You have to treat it as something that you didn&apos;t write yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:48&lt;/strong&gt; · You need to test it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:49&lt;/strong&gt; · You need to know how to make it secure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:53&lt;/strong&gt; · So that requires lots of knowledge that for us is second nature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:58&lt;/strong&gt; · But for somebody who is completely new to coding, they might not even know that there are security risks and how to deal with them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:11&lt;/strong&gt; · So what should people that come from a completely different field do in order to not get caught out?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:19&lt;/strong&gt; · RODY DAVIS: So one of the things that I would suggest is if you&apos;re coming into this with not a lot of experience, or maybe you did it a long time ago, or just changing industries or whatever, that&apos;s where our other Gemini tools are really useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:34&lt;/strong&gt; · So Gemini Deep Research is a great way to get answers and more questions to the things that you don&apos;t even know to ask.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:44&lt;/strong&gt; · So if you literally put in there, How do I get my app into the Google Play Store?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:50&lt;/strong&gt; · What are the list of things I need to do?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:52&lt;/strong&gt; · It would do a full research page on exactly, step by step, on the ways you get your metadata done for the screenshots, ways to do your review, how to upload the Play Store, step by step, often tutorials.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:09&lt;/strong&gt; · So use tools like that to really help you get around the knowledge gaps, because you&apos;ll be surprised at how straightforward that is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:20&lt;/strong&gt; · Firebase Studio is just a development platform, an IDE, so we&apos;re just one part of the puzzle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:26&lt;/strong&gt; · But I would definitely suggest Gemini and Gemini Deep Research for that kind of stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:32&lt;/strong&gt; · PETER FRIESE: Yeah, I remember we had this discussion when we were at Google I/O Connect in Berlin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:39&lt;/strong&gt; · And you told me about how you do research with the tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:43&lt;/strong&gt; · And then I tried it out and asked it to research restaurants near where I live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:48&lt;/strong&gt; · And it was really great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:49&lt;/strong&gt; · So it came out with this really nicely written article that almost was like something that you would read in a restaurant review magazine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:58&lt;/strong&gt; · And I was like, oh, you should totally check out this one here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:01&lt;/strong&gt; · RODY DAVIS: Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:02&lt;/strong&gt; · PETER FRIESE: So yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:02&lt;/strong&gt; · RODY DAVIS: And also, NotebookLM is great too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:04&lt;/strong&gt; · Yeah, you can basically take the research docs and then put them in a NotebookLM and ask more directed questions there too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:10&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:11&lt;/strong&gt; · ALEXANDER NOHE: I&apos;ve been putting in my financial statements into NotebookLM because I saw a guide on this online.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:17&lt;/strong&gt; · I was like, oh, I&apos;ll just try it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:18&lt;/strong&gt; · And then I was like, wow, you&apos;re really good at doing this thing, like paying your bills on time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:22&lt;/strong&gt; · And I was like, oh, thanks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:24&lt;/strong&gt; · RODY DAVIS: Yeah, I uploaded 62 PDFs for me and my wife for getting a house.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:29&lt;/strong&gt; · And so we had all of the disclosures that they gave us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:33&lt;/strong&gt; · And tools like this make it so easy to scan and parse long documents of structured texts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:40&lt;/strong&gt; · As a human, it&apos;s really hard to read that in a quick manner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:45&lt;/strong&gt; · But yeah, use these tools for what they&apos;re good at because there&apos;s a lot of tutorials out there on how to publish your app on the App Store.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:51&lt;/strong&gt; · But usually, there&apos;s a ton of information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:55&lt;/strong&gt; · It&apos;s spread across Stack Overflow and Stack Exchange and Reddit and search.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:00&lt;/strong&gt; · But Gemini Deep Research basically takes all that and normalizes it into one place and has a series of steps that are often going to be pretty good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:09&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:10&lt;/strong&gt; · ALEXANDER NOHE: If you ever need an ego boost, like you just wake up one day, and you&apos;re like, man, I&apos;m not feeling it, put your resume into NotebookLM, and have it generate a podcast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:20&lt;/strong&gt; · It&apos;ll be like, this guy-- PETER FRIESE: Oh, yeah, that&apos;s fantastic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:22&lt;/strong&gt; · RODY DAVIS: Oh, I thought you were going to say, have it roast you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:25&lt;/strong&gt; · ALEXANDER NOHE: Oh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:26&lt;/strong&gt; · Ooh, that actually might be fun too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:28&lt;/strong&gt; · Can you guide it like that and be like, roast this guy?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:30&lt;/strong&gt; · RODY DAVIS: I&apos;m sure it can.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:31&lt;/strong&gt; · There is a system prompt setting for it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:36&lt;/strong&gt; · PETER FRIESE: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:37&lt;/strong&gt; · ALEXANDER NOHE: Definitely going to be looking this up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:38&lt;/strong&gt; · PETER FRIESE: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:39&lt;/strong&gt; · So I know that lots of people asked about how they can get their feature requests to the team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:47&lt;/strong&gt; · So I wanted to show this here, which is the UserVoice that the Firebase team is using.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:53&lt;/strong&gt; · So this is a site that&apos;s public, you can go to, and you can search for anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:00&lt;/strong&gt; · And I did a quick search for Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:02&lt;/strong&gt; · And you can see that there&apos;s already a couple of people who asked about Flutter and Android Studio in Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:14&lt;/strong&gt; · And you can submit your own feature request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:19&lt;/strong&gt; · But chances are that there already are people who are interested in the same thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:24&lt;/strong&gt; · So you can sign in and upvote the thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:27&lt;/strong&gt; · And then, eventually, we&apos;ll have the resources and time to work on these.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:33&lt;/strong&gt; · And you can go through all of these, and you&apos;ll see that there are a bunch of those feature requests that are actually implemented in the product.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:45&lt;/strong&gt; · So that is one way to reach out to the team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:50&lt;/strong&gt; · Rody, is there any other way how people could get in touch with the Firestore team and say, hey, I have this really fantastic idea?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:59&lt;/strong&gt; · Here it is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:59&lt;/strong&gt; · Here is the prototype or whatever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:01&lt;/strong&gt; · How can they do that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:02&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:03&lt;/strong&gt; · For the Studio team, I think the best place would be-- the forum is great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:08&lt;/strong&gt; · So we also have a forum.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:10&lt;/strong&gt; · We check Reddit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:11&lt;/strong&gt; · We check Twitter and LinkedIn.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:15&lt;/strong&gt; · The best place would be to have official place, like on the forum or tools like that, because that&apos;s where we can easily close out things when they&apos;re resolved, point people to the correct resources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:28&lt;/strong&gt; · And then we also have the Firebase community that we&apos;re also a part of as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:34&lt;/strong&gt; · Yeah, if you have any issues, we are happy to answer them and help them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:40&lt;/strong&gt; · One of the interesting things about Firebase Studio is people will come with Firebase issues or Gemini issues or app issues.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:49&lt;/strong&gt; · And it&apos;s literally infinite the amount of things that could be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:54&lt;/strong&gt; · So yeah, but we&apos;ll easily be able to direct you to the right people.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:58&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:59&lt;/strong&gt; · And we&apos;ll put links to all those resources in the video description so people can find it after this live stream ends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:08&lt;/strong&gt; · Nohe, did we cover most questions on the live stream?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:12&lt;/strong&gt; · ALEXANDER NOHE: I think so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:14&lt;/strong&gt; · There was a question about Data Connect that I saw come through.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:18&lt;/strong&gt; · Maybe we should do another Data Connect live stream eventually?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:21&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:22&lt;/strong&gt; · ALEXANDER NOHE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:22&lt;/strong&gt; · PETER FRIESE: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:24&lt;/strong&gt; · We should do one where we have people from all those teams and then make them work together and implement an application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:31&lt;/strong&gt; · ALEXANDER NOHE: We should do it like the holiday special that we planned last year.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:34&lt;/strong&gt; · PETER FRIESE: Yeah, I know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:35&lt;/strong&gt; · I know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:36&lt;/strong&gt; · And maybe we could reuse-- ALEXANDER NOHE: That would have saved the year.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:38&lt;/strong&gt; · PETER FRIESE: Maybe we could reuse that really funny skit that we did.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:42&lt;/strong&gt; · ALEXANDER NOHE: Yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:43&lt;/strong&gt; · Rody, were you in on the holiday special that we talked about?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:47&lt;/strong&gt; · RODY DAVIS: I know what you&apos;re talking about, but I wasn&apos;t in on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:49&lt;/strong&gt; · ALEXANDER NOHE: OK, great, great, great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:50&lt;/strong&gt; · Yeah, yeah, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:51&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:52&lt;/strong&gt; · Ah, that would be fun to do on a live stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:56&lt;/strong&gt; · PETER FRIESE: Yeah, absolutely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:58&lt;/strong&gt; · Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:59&lt;/strong&gt; · And then, because it&apos;s a live stream, it&apos;s a live stream, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:03&lt;/strong&gt; · Just saying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:05&lt;/strong&gt; · All right, Rody, we&apos;re almost at time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:09&lt;/strong&gt; · Is there anything that we didn&apos;t discuss that you are dying to tell people about?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:15&lt;/strong&gt; · RODY DAVIS: I think the biggest thing would be, just come try it out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:19&lt;/strong&gt; · If you haven&apos;t used Firebase Studio recently, it&apos;s probably changed in the last week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:24&lt;/strong&gt; · So we&apos;re always working on shipping updates and making it better and rapidly improving it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:31&lt;/strong&gt; · And even down to the day, if you&apos;re having issues with Gemini, the issue might get resolved the next day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:38&lt;/strong&gt; · And so if anybody is having issues opening workspaces, definitely let us know on the forums, and we&apos;ll see if we can get that resolved pretty quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:47&lt;/strong&gt; · But I&apos;ll just say, also, I really like using Gemini CLI inside Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:52&lt;/strong&gt; · It is super underrated how powerful it is to have a full VM that you can just ask and iterate on questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:59&lt;/strong&gt; · And even running Gemini CLI alongside the regular Gemini chat and having it update our previews, it&apos;s kind of a magical experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:09&lt;/strong&gt; · We can have them all running task in different threads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:12&lt;/strong&gt; · So yeah, no, it&apos;s a fun time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:14&lt;/strong&gt; · PETER FRIESE: Is there a place where people can go and see the release notes?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:20&lt;/strong&gt; · RODY DAVIS: Yeah, I think that we have the main Firebase release notes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:24&lt;/strong&gt; · We probably most often put it on the blog and then share it on social channels and things like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:31&lt;/strong&gt; · We&apos;re usually pretty good about making little short snippets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:33&lt;/strong&gt; · PETER FRIESE: Yeah, I was thinking about Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:36&lt;/strong&gt; · Is there a place where I can go and say, OK, so what has changed in the last week?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:41&lt;/strong&gt; · RODY DAVIS: Yeah, we have a general roadmap and some other things that I&apos;ve shown.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:46&lt;/strong&gt; · But I&apos;m not aware of anything other than just updating our docs and releasing social posts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:53&lt;/strong&gt; · PETER FRIESE: OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:54&lt;/strong&gt; · Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:54&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:55&lt;/strong&gt; · Yeah, we&apos;ll post all the links in the description.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:59&lt;/strong&gt; · RODY DAVIS: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:00&lt;/strong&gt; · PETER FRIESE: And I think, with that, it is time to say thank you, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:05&lt;/strong&gt; · Thanks for coming on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:07&lt;/strong&gt; · Thanks for showing us some of the cool new features and taking questions from Nohe and me and the audience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:15&lt;/strong&gt; · RODY DAVIS: Thanks for having me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:16&lt;/strong&gt; · PETER FRIESE: Yeah, we&apos;ll bring you back for the grand thing that we&apos;ll roast the entire team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:24&lt;/strong&gt; · RODY DAVIS: Nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:25&lt;/strong&gt; · PETER FRIESE: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:26&lt;/strong&gt; · RODY DAVIS: That&apos;s great, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:28&lt;/strong&gt; · PETER FRIESE: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:28&lt;/strong&gt; · Nohe, do you have any final words before I click the button to send everybody back home?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:35&lt;/strong&gt; · ALEXANDER NOHE: No, click the button, Peter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:37&lt;/strong&gt; · PETER FRIESE: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:38&lt;/strong&gt; · RODY DAVIS: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:38&lt;/strong&gt; · PETER FRIESE: Thanks for tuning in, everybody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:40&lt;/strong&gt; · Thanks for joining us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:41&lt;/strong&gt; · And go try it out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:43&lt;/strong&gt; · Let us know what you think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:45&lt;/strong&gt; · And we&apos;ll post all the links so you can send your feedback.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:47&lt;/strong&gt; · Thanks, and see you in the next one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:50&lt;/strong&gt; · RODY DAVIS: All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:51&lt;/strong&gt; · Bye.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:54&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>From prototype to IDE in Firebase Studio</title><link>https://rodydavis.com/videos/firebase-studio/from-prototype-to-ide-in-firebase-studio/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/from-prototype-to-ide-in-firebase-studio/</guid><description>Discover how to customize code and leverage Gemini within Firebase Studio&apos;s full IDE and VM environment.</description><pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;From prototype to IDE in Firebase Studio&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;9CLNxcM1tbo&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Take advantage of Firebase Studios IDE and VM features! Rody, a Developer Relations Engineer for Firebase Studio shares how you can customize code and use Gemini to make edits.&lt;/p&gt;
&lt;p&gt;Firebase Studio → https://goo.gle/firebasestudio&lt;/p&gt;
&lt;p&gt;#Firebase #NixOS&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned:,  Firebase,&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · SPEAKER: If you&apos;re new to Firebase Studio, you may not realize that you have a full IDE and VM at your fingertips.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:07&lt;/strong&gt; · To take advantage of this, we can just go up here to the top right and click Switch to Code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:13&lt;/strong&gt; · This allows us to have full access to the code that the prototyper generates and lets you customize it any way you see fit, including custom integrations or working with external data sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:25&lt;/strong&gt; · The file that the prototyper uses for editing is the page.tsx inside the App Directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:31&lt;/strong&gt; · And you can see that we have the code for React here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · We can go to the preview and see the same preview that I was showing in the prototyper, but now we have it inside of the IDE.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:41&lt;/strong&gt; · We can also still take full advantage of Gemini in Firebase to make changes and edits to our files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:47&lt;/strong&gt; · For example, we can add a table under the calculator to show a list of possible tip options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · We can see that Gemini picked up the file that we are currently editing, and we&apos;ll soon suggest an edit which we can then commit and save.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03&lt;/strong&gt; · Next, we can hit Reload if it hasn&apos;t already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06&lt;/strong&gt; · And now we&apos;ll see the table at the bottom of our web preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · We can verify it&apos;s working by entering an amount, updating some values-- and awesome!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16&lt;/strong&gt; · We can see that the table is functional.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · Gemini inside of Firebase has full control of your project, so it can read and write files that you give it access to and run terminal commands.&lt;/p&gt;
</content:encoded></item><item><title>Import existing projects into Firebase Studio</title><link>https://rodydavis.com/videos/firebase-studio/import-existing-projects-into-firebase-studio/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/import-existing-projects-into-firebase-studio/</guid><description>Learn how to import existing codebases into Firebase Studio from various sources like GitHub, GitLab, and more to get started with your project.</description><pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Import existing projects into Firebase Studio&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;31GsuvcRcXk&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Get started with your own codebase in Firebase Studio. Rody, a Developer Relations Engineer for Firebase Studio shares how you can import your project from a variety of sources such as Github, GitLab, and more!&lt;/p&gt;
&lt;p&gt;Firebase Studio → https://goo.gle/firebasestudio&lt;/p&gt;
&lt;p&gt;#Firebase #Git&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned:, Firebase&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · SPEAKER: If you just heard about Firebase Studio and you want to get started with your own code base, well, we have you covered.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:04&lt;/strong&gt; · Go ahead and go to studio.firebase.google.com and then click on Import Repo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · This screen allows you to pull in your project from a variety of public and private sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · We support GitHub, Bitbucket, GitLab, or even uploading your project from a zip file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · In this case, we can upload the quickstart.js Firebase sample.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:24&lt;/strong&gt; · By default, when you enter the URL, it&apos;ll give you a default name for your workspace, which you can change to whatever you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:30&lt;/strong&gt; · When we click on Import, it&apos;s going to go ahead and build our VM for us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · Once that&apos;s done, it&apos;s going to launch the IDE that we can see our code that was downloaded from the source.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · If you don&apos;t already have one, it will create a dev.nix for you on the first launch of your workspace, which you can use to customize the specific dependencies for this project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:55&lt;/strong&gt; · Gemini will also appear.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · And, by default, if you don&apos;t have a dev.nix, you&apos;re not going to see any web previews.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01&lt;/strong&gt; · But we have documentation on how to get that set up for you to get started.&lt;/p&gt;
</content:encoded></item><item><title>How Firebase Studio accelerates development</title><link>https://rodydavis.com/videos/firebase-studio/how-firebase-studio-accelerates-development/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/how-firebase-studio-accelerates-development/</guid><description>Firebase Studio is a powerful new tool that enables users to quickly prototype interactive applications with Next.js and Genkit, using AI for blueprint generation, fast customization, and one-click deployment.</description><pubDate>Tue, 29 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;How Firebase Studio accelerates development&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;llByU3YZvew&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Get an exciting first look at Firebase Studio—a powerful new tool designed for interactive prototyping with Next.js and Genkit. In this video, Rody from the Firebase team shows just how quickly you can kickstart a project, using AI to instantly generate a blueprint for a tipping calculator app. With fast customization, one-click deployment, and auto-provisioning of Firebase projects and Gemini API keys, it’s never been easier to go from idea to working prototype.&lt;/p&gt;
&lt;p&gt;Try it for yourself → https://studio.firebase.google.com&lt;/p&gt;
&lt;p&gt;#GoogleCloudNext #Firebase&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned: Firebase Studio&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Hi, I&apos;m Rodie. I&apos;m a senior developer relations engineer on the Firebase Studio team. And I&apos;m going to give you an first look at Firebase Studio. One of the cool things that we&apos;re launching is a way to do interactive prototyping for Nex.js and Genkit. We can go in and simply say, I want to build a tipping app calculator. When we hit prototype with AI, it&apos;s going to give us a nice blueprint. This is all ready for you to try out now at studio.firebase.google.com. Check out this awesome blueprint. We can modify our features if we want. Then we can go ahead and hit prototype.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:30&lt;/strong&gt; · This is one of the things that we&apos;re really excited for you to try out. Please reach out to us if you have any feedback.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · Super easy to share. You can have some awesome ways to get into the code. We have oneclick publish to even Firebase app hosting. If there&apos;s any features that need a Gemini API key, we&apos;ll even autoprovision a Firebase project and Gemini API key for you. So it looks like we just generated really quickly. This is all live and no edits. Say God.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00&lt;/strong&gt; · Thanks for watching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · [Music]&lt;/p&gt;
</content:encoded></item><item><title>Introducing Firebase Studio</title><link>https://rodydavis.com/videos/firebase-studio/introducing-firebase-studio/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/introducing-firebase-studio/</guid><description>Firebase Studio is an AI-powered development environment designed to help developers quickly build and ship production-quality, full-stack AI applications, from APIs to frontends and backends.</description><pubDate>Fri, 30 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Introducing Firebase Studio&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;vVAui3_rvD8&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Discover Firebase Studio, an AI-powered agentive development environment that helps developers build and ship production quality full stack AI apps. From APIs to backends, frontends, mobile, and more, Firebase Studio is the tool you need to bring your ideas to life faster than ever.&lt;/p&gt;
&lt;p&gt;Chapters:
0:00 - Firebase Studio overview
0:30 - Prototyping with AI
2:08 - Code mode and customization
2:36 - Preview and deploy
3:06 - Recap&lt;/p&gt;
&lt;p&gt;Resources:
Check out the docs → https://goo.gle/4micjGg&lt;/p&gt;
&lt;p&gt;Subscribe to Firebase → https://goo.gle/Firebase&lt;/p&gt;
&lt;p&gt;#Firebase&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned: Firebase,&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Firebase Studio overview&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hey, Firebase developers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:06&lt;/strong&gt; · Are you&apos;re ready to build full stack web apps faster than ever?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:09&lt;/strong&gt; · We&apos;re excited to introduce Firebase Studio, your AI-powered development environment now available in preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:17&lt;/strong&gt; · Firebase Studio not only helps you go from idea to fully functional app in record time by leveraging the power of generative AI, it also is a fully fledged development environment accessible from anywhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · Let&apos;s dive in.&lt;/p&gt;
&lt;h3&gt;Prototyping with AI&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:31&lt;/strong&gt; · Let&apos;s start with the App Prototyping agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · Imagine you have an idea for an app that generates an inspirational quote and then an image based on that quote.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · Instead of starting from scratch, we can just describe our idea into Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:43&lt;/strong&gt; · Log in to your Google account and then open up Firebase Studio to get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:48&lt;/strong&gt; · Enter a description of the app that you want to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:51&lt;/strong&gt; · In this case, we&apos;ll use the prompt to prototype an app that will generate images and texts with generative AI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:57&lt;/strong&gt; · Click on Prototype with AI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:59&lt;/strong&gt; · Firebase Studio generates an app blueprint based on your prompt, returning a proposed app name, required features, and style guidelines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · This is the stage where you can really start to think about what you want your app to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11&lt;/strong&gt; · You can suggest new features or UI themes, and it will generate a new blueprint for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16&lt;/strong&gt; · If you do not want an AI feature or want something completely different, edit the PRD and remove it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · For example, let&apos;s change the app name and color scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25&lt;/strong&gt; · Make sure to be as detailed as possible if you have something specific in mind.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33&lt;/strong&gt; · Click Prototype this App and watch the magic happen as Firebase Studio App Prototyping agent scaffolds and builds out your app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:40&lt;/strong&gt; · Because we asked for an AI feature, Firebase Studio prompts us for a Gemini API key.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45&lt;/strong&gt; · We can then click Auto Generate to get a key and a brand new Firebase project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:49&lt;/strong&gt; · Now let&apos;s take a look at what we got.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:55&lt;/strong&gt; · Click on Generate to create a new image and quote.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01&lt;/strong&gt; · If you want to check out the Firebase project it just created, click on the New name in the app header.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:07&lt;/strong&gt; · Now let&apos;s take a look at the code.&lt;/p&gt;
&lt;h3&gt;Code mode and customization&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:09&lt;/strong&gt; · Firebase Studio gives you a fully functional code editor powered by Code OSS, the open source fork of VS Code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · You have full control over the generated code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:18&lt;/strong&gt; · Want to tweak the UI, add a new feature, or debug the code?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:21&lt;/strong&gt; · No problem, we have covered.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:24&lt;/strong&gt; · You can use the built-in chat functionality to ask questions and get suggestions from our AI Assistant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:29&lt;/strong&gt; · You can even change the built-in model if you have a preferred one or want to try out an experimental one from the Google AI API.&lt;/p&gt;
&lt;h3&gt;Preview and deploy&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:37&lt;/strong&gt; · You can see your changes in real time with the built-in preview.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · And when you&apos;re ready to share your app with the world, you can publish it with Firebase App Posting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:48&lt;/strong&gt; · After publishing, you can continue to make changes and roll them out when you&apos;re ready.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:53&lt;/strong&gt; · Now, while we showed off the App Prototyping agent flow, that&apos;s not all you can do with Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:58&lt;/strong&gt; · You can import existing projects from a zip file or source control, or start with one of our many Firebase Studio templates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:05&lt;/strong&gt; · Firebase Studio is an agentic cloud-based development environment that helps you build and ship production quality full stack AI apps, including APIs, back-ends, front-ends, mobile, and so much more.&lt;/p&gt;
&lt;h3&gt;Recap&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:16&lt;/strong&gt; · From AI-powered prototyping to an all-in-one, cloud-based development environment accessible from anywhere, it has everything you need to help make your app idea a reality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:24&lt;/strong&gt; · If you have an idea for an app that you&apos;ve been wanting to build, try it out now with the App Prototyping agent with Firebase Studio and share with us how it went.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:32&lt;/strong&gt; · We cannot wait to see what you&apos;re going to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:34&lt;/strong&gt; · Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:36&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Start prototyping with Firebase Studio</title><link>https://rodydavis.com/videos/firebase-studio/start-prototyping-with-firebase-studio/</link><guid isPermaLink="true">https://rodydavis.com/videos/firebase-studio/start-prototyping-with-firebase-studio/</guid><description>Learn how to quickly build and deploy a fully functional app, such as a drawing canvas, using natural language prompts and Firebase Studio&apos;s Prototype with AI feature.</description><pubDate>Wed, 07 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Start prototyping with Firebase Studio&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;hB1BdhINRNE&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Did you know you can build an app with Firebase Studio? Rody, a Developer Relations Engineer for Firebase Studio shares how to build and deploy an app using the prototype feature.&lt;/p&gt;
&lt;p&gt;Firebase Studio → https://studio.firebase.google.com&lt;/p&gt;
&lt;p&gt;#Firebase  #VibeCoding #AI&lt;/p&gt;
&lt;p&gt;Speaker: Rody Davis
Products Mentioned:,  Firebase&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · SPEAKER: Have you ever wanted to build an app but wasn&apos;t quite sure where to start?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:03&lt;/strong&gt; · Well, Firebase Studio has you covered.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:05&lt;/strong&gt; · With Firebase Studio, you can get started with a natural language prompt that generates a full application backed by Next.js and Genkit that you can deploy with one click.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · Let&apos;s get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:15&lt;/strong&gt; · Head over to studio.firebase.google.com.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:18&lt;/strong&gt; · And let&apos;s enter a natural prompt such as &amp;quot;build an interactive canvas drawing application.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · And then once you&apos;re done with the prompt, you can go ahead and hit Prototype with AI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · The first thing that it&apos;s going to do is generate a blueprint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · This blueprint is an overview of your application and all the features that are going to be included.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:41&lt;/strong&gt; · Make sure to edit it if there&apos;s anything that you want to remove such as AI features or something else that you didn&apos;t intend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · Once you&apos;re happy with the blueprint, you can go ahead and hit Prototype This App.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:57&lt;/strong&gt; · Once that&apos;s done, you&apos;ll see the preview become live with the app that we just asked it to generate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03&lt;/strong&gt; · In this case, since it&apos;s a Canvas application, we can go ahead and draw.&lt;/p&gt;
</content:encoded></item><item><title>Create simple, beautiful UI with Flutter</title><link>https://rodydavis.com/videos/flutter/create-simple-beautiful-ui-with-flutter/</link><guid isPermaLink="true">https://rodydavis.com/videos/flutter/create-simple-beautiful-ui-with-flutter/</guid><description>Learn how to transform a generic Flutter app into a beautiful, branded, and highly polished user experience using simple design techniques focusing on typography, color, motion, and animation.</description><pubDate>Wed, 11 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Create simple, beautiful UI with Flutter&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;eBVMfS7MayI&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Beautiful experiences and great UI don&apos;t happen by themselves, but that doesn&apos;t mean they have to be difficult! Watch two design experts apply simple, straightforward, incremental techniques to take a well-built but boring-looking app and turn it into something memorable.&lt;/p&gt;
&lt;p&gt;Resources:
Codelab → https://goo.gle/3Mn16SF&lt;/p&gt;
&lt;p&gt;Speakers: Khanh Nguyen, Rody Davis&lt;/p&gt;
&lt;p&gt;Watch more:
All Google I/O 2022 Sessions → https://goo.gle/IO22_AllSessions
Flutter at I/O 2022 playlist → https://goo.gle/IO22_Flutter
All Google I/O 2022 workshops → https://goo.gle/IO22_Workshops&lt;/p&gt;
&lt;p&gt;Subscribe to Flutter! → https://goo.gle/FlutterYT&lt;/p&gt;
&lt;p&gt;#GoogleIO&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi, and welcome to our workshop about taking your app from boring to beautiful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:08&lt;/strong&gt; · My name is Rody, and I&apos;m a Developer Relations Engineer on the Material Design team at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:13&lt;/strong&gt; · KHANH NGUYEN: And hi, I&apos;m Khanh, a Developer Relations Engineer on the Flutter team at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:18&lt;/strong&gt; · In this workshop, we&apos;ll be walking through a codelab discussing a few tips and tricks that you can integrate into a Flutter app to take it from boring to beautiful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:25&lt;/strong&gt; · Now, Rody, why are we doing this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:26&lt;/strong&gt; · RODY DAVIS: When you&apos;re building with Flutter, one of the things that you may be facing is the app will feel very generic when you get done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · Material provides some beautiful widgets that we can build with, but we have a lot more to do to make it feel like it&apos;s our own personal brand and customizing the theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:41&lt;/strong&gt; · So today, we&apos;re going to be going over a lot of things that can really add that extra polish, such as typography, color, motion, animation, and overall responsiveness that we can make our app feel home at all the platforms that Flutter supports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · So, Khanh, what kind of app are we going to be building today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:59&lt;/strong&gt; · KHANH NGUYEN: That&apos;s a great question, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01&lt;/strong&gt; · So once you have your Flutter environment set up, and you clone the codelab repo, and go ahead and run the starter app, you&apos;ll see that, for this workshop, we&apos;ll be working on an app that we like to call myartist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · And we&apos;ll be implementing the changes here in order to make it beautiful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18&lt;/strong&gt; · So myartist is a social music app that lets fans keep up to date with their favorite artists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · So while we&apos;re here, let&apos;s go ahead and take a quick tour through myartist and see what we&apos;re going to be working with today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30&lt;/strong&gt; · Here, we see the home screen for myartist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32&lt;/strong&gt; · And so, at the top, you have a nice, little banner, updates from your favorite artists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · You have the recently played playlists tiles, as well as the top songs for today, new releases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44&lt;/strong&gt; · On the side, there is a navigation rail that has your Home screen, which we&apos;re currently on, Playlists, which has a grid of playlists, as well as the bright blue app bar that we know and love, as well as an Artists screen, which has a grid of your favorite artists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01&lt;/strong&gt; · And so this is what it looks like on desktop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:03&lt;/strong&gt; · Let&apos;s scale this app down to be, say, mobile size.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:08&lt;/strong&gt; · So when we scale it down, you&apos;ll see that it doesn&apos;t look really great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:12&lt;/strong&gt; · It&apos;s the same layout as before, but now everything is just kind of squeezed together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:18&lt;/strong&gt; · It doesn&apos;t look super great and, I mean, not to mention, the navigation rail-- the text is now gone, and these icons are identical, so you don&apos;t even know what you&apos;re getting when you tap on an icon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:29&lt;/strong&gt; · So don&apos;t worry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:31&lt;/strong&gt; · This doesn&apos;t look super great, but we&apos;ll spend time today making it look better for mobile and on desktop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:37&lt;/strong&gt; · So with that said, Rody, what&apos;s one of the first things that we should do here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:42&lt;/strong&gt; · RODY DAVIS: So I think one of the first things that we can do is start with our typography.&lt;/p&gt;
&lt;h3&gt;Google Fonts To Import the Material Icons&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · We can use Google Fonts to import the Material icons, as well as a custom font.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:51&lt;/strong&gt; · Now, the Material icons are included as part of the Flutter SDK, so we can actually choose some new icons that make sense for our navigation rail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:59&lt;/strong&gt; · KHANH NGUYEN: Yeah, so let&apos;s go ahead and do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · And so, in the first step of our codelab here, it just tells us to replace the icons to be icons that give the user a little bit more information on the tab that they&apos;re clicking on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:13&lt;/strong&gt; · So for those of you at home, you can just replace the icon itself, but for us here, I&apos;m just going to replace the entire destinations list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:20&lt;/strong&gt; · It&apos;s going to look exactly the same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:22&lt;/strong&gt; · But for the sake of the screencast, this makes it a little bit easier than copying and pasting each icon individually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:28&lt;/strong&gt; · So jumping into lib/src/shared/router.dart, I&apos;m just going to replace the entire destinations list, but you can just replace the icons themselves individually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:39&lt;/strong&gt; · So I&apos;ll just save that there and go ahead and hot reload our app, and we can jump over to our app and see what that looks like.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:49&lt;/strong&gt; · All right, so now the icons are no longer the same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:52&lt;/strong&gt; · Let&apos;s see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:52&lt;/strong&gt; · Let&apos;s go down to the mobile view.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:54&lt;/strong&gt; · Even though it&apos;s not ideal to have the navigation rail on the side like that, you now at least get icons that give more detail than identical icons that we had before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:04&lt;/strong&gt; · All right, so that does that for that first step of the codelab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:10&lt;/strong&gt; · So next up of this workshop is to talk about choosing fonts and how we can choose fonts thoughtfully.&lt;/p&gt;
&lt;h3&gt;Choosing Fonts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:15&lt;/strong&gt; · So fonts will set the personality of your application, and that&apos;s why it&apos;s so crucial to pick the right font.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:21&lt;/strong&gt; · So when selecting a font, here are a few things to consider, number one being serif versus sans serif.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:28&lt;/strong&gt; · So serif fonts are this decorative sort of-- they have these strokes and tails at the end of the letters and tends to be perceived as more formal, whereas sans-serif fonts don&apos;t have those decorative strokes at the end, or those tails, and can be perceived as more informal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:45&lt;/strong&gt; · And for long blocks of text, generally, we&apos;ll see more of those serif fonts, but it can be a mixture, especially on the web and depending on the personality of your application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:56&lt;/strong&gt; · However, in this case, we&apos;ll probably want a sans-serif font because we&apos;re a social music app, and we want some fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:02&lt;/strong&gt; · We want some personality, some more excitement, energetic, and playful sort of emotion to be tied to our fonts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:10&lt;/strong&gt; · In addition to that, when picking an all-caps font, consider this very carefully because using all-caps font can be useful for calling attention to small amounts of text, like, for example, headlines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:22&lt;/strong&gt; · But when it&apos;s overused, it can be perceived as you&apos;re shouting at the user, and they might just completely ignore that headline entirely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:32&lt;/strong&gt; · Another thing to think about is using title case versus sentence case.&lt;/p&gt;
&lt;h3&gt;Title Case versus Sentence Case&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:36&lt;/strong&gt; · So title case is when the first letter of each word is capitalized, whereas sentence cases when you only capitalize the first letter of the word, as well as proper nouns.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:46&lt;/strong&gt; · So with title casing, that&apos;s considered more formal, whereas sentence casing is considered more conversational and informal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:53&lt;/strong&gt; · Other things I want to think about is kerning, which is that spacing between each letter, the line length, which is how long your lines run across the screen, and line height, so how tall the lines of your text are.&lt;/p&gt;
&lt;h3&gt;Kerning&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;6:06&lt;/strong&gt; · So too much or too little of any of these things can make the app less readable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:11&lt;/strong&gt; · So, for example, if you&apos;ve ever read a very long string of text and lost your place when you jumped to the next line, that&apos;s probably because the line length was a little too long.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:19&lt;/strong&gt; · So that&apos;s when you know you should cut down that line length.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:23&lt;/strong&gt; · And so, for our app today, we&apos;ve decided on a font called Montserrat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:28&lt;/strong&gt; · So let&apos;s go ahead and jump over to the Google Fonts page for it and take a look at it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:33&lt;/strong&gt; · So like we mentioned before, Rody and I went with Montserrat because we wanted a sans-serif font, and there&apos;s just a lot of styles available for Montserrat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:41&lt;/strong&gt; · So, essentially, we knew that if we picked this font, there would be a lot of variety, and we could use it for a lot of different use cases within the app, and not have to worry about picking a different font to go along with the one that we had already picked.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:55&lt;/strong&gt; · So that&apos;s the font that we&apos;re going to use today, and how are we going to add it to our app?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:00&lt;/strong&gt; · So we can add to our app by using the Google Fonts package for Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:05&lt;/strong&gt; · So what we want to do is first go ahead and add the google_fonts package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:09&lt;/strong&gt; · And we can do that by running Flutter pub add google_fonts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:12&lt;/strong&gt; · So jumping over to VS Code now, let&apos;s go here and add our google_fonts package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:20&lt;/strong&gt; · So while that&apos;s adding there, let me jump back to the codelab and touch on this piece here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:25&lt;/strong&gt; · So for those of you who are building for macOS, do make sure that your entitlements files allow for network fetching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:32&lt;/strong&gt; · So we&apos;ve already handled this for you in the codelab starter code, so you don&apos;t have to worry about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:37&lt;/strong&gt; · But if you want to use your Google Fonts with your own apps, do make sure you have these lines added to your entitlements files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:44&lt;/strong&gt; · So, by the way, macOS is now in stable, so that&apos;s super exciting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:49&lt;/strong&gt; · All right, let&apos;s go ahead and jump back to VS Code and double-check.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:53&lt;/strong&gt; · All right, so that&apos;s been added.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:56&lt;/strong&gt; · All right, we should be good there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:57&lt;/strong&gt; · So now let&apos;s actually make use of the package itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:01&lt;/strong&gt; · So we want to add the google_fonts package to our app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:04&lt;/strong&gt; · We want to import it into lib/src/shared/extensions.dart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:08&lt;/strong&gt; · All right, so we can jump in here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:10&lt;/strong&gt; · Conveniently, extensions.dart is already open here, so we&apos;ll go up to the top and import the google_fonts package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:18&lt;/strong&gt; · All right, so we&apos;ll save that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:19&lt;/strong&gt; · And what do we do next?&lt;/p&gt;
&lt;h3&gt;Code Lab&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;8:20&lt;/strong&gt; · Let&apos;s check the codelab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:22&lt;/strong&gt; · All right, so we just pull in that Montserrat TextTheme and wrap it around our existing TextTheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:27&lt;/strong&gt; · All right, copy and paste it here into our-- whoop, I did not want to go to myartist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:34&lt;/strong&gt; · So go ahead and paste this here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:36&lt;/strong&gt; · So, Rody, looking at the file here, there&apos;s a lot of different TextStyles.&lt;/p&gt;
&lt;h3&gt;Text Styles&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;8:43&lt;/strong&gt; · Like, what are these?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:44&lt;/strong&gt; · Like, textTheme.textLarge, text-- titleMedium?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:48&lt;/strong&gt; · RODY DAVIS: Yeah, so these are the new Material 3 typography tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:52&lt;/strong&gt; · This should make it a lot easier to build with typography values and apply them to specific widgets throughout your Flutter application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:00&lt;/strong&gt; · These are just different semantic meanings, such as headlineLarge, Small, Medium, and they should match up pretty well with the type of content that you&apos;re trying to present.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:08&lt;/strong&gt; · You can also, in addition to this, use opacity to help reduce emphasis of text that you don&apos;t want to be screaming at the user and to kind of fade into the background.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:16&lt;/strong&gt; · As well as a quick note on the google_fonts package, when you import it, you can import anything from Google Fonts.&lt;/p&gt;
&lt;h3&gt;Google Fonts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;9:21&lt;/strong&gt; · And you can optimize it at build time for production by including the font in your asset directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:27&lt;/strong&gt; · KHANH NGUYEN: Yes, and the reason why I love the google_fonts package is because sometimes I want to try a variety of different fonts, and I don&apos;t need to download the individual fonts individually before I&apos;ve already settled on one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:39&lt;/strong&gt; · So that&apos;s a great call-out there, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:42&lt;/strong&gt; · All right, so that takes us to the end of typography.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:44&lt;/strong&gt; · Let&apos;s jump to the next step in our codelab here and talk about setting the theme.&lt;/p&gt;
&lt;h3&gt;Setting the Theme&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;9:49&lt;/strong&gt; · So, Rody, take us away on this one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:51&lt;/strong&gt; · RODY DAVIS: Of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:52&lt;/strong&gt; · So I&apos;m not a designer, and one of the things that building with Flutter is you&apos;re dropped with a color theme that&apos;s blue on blue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:00&lt;/strong&gt; · Now, this is great for most applications, but we can do better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:03&lt;/strong&gt; · We can choose a color scheme that makes sense for our brand and our personal app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:07&lt;/strong&gt; · So we need to first start off with a ThemeProvider class.&lt;/p&gt;
&lt;h3&gt;Theme Provider&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;10:11&lt;/strong&gt; · This is the foundation for applying a theme to our application, and it wraps up all the different ThemeData widgets for, let&apos;s say, Navigation Rail, the Drawer and Scaffold.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:23&lt;/strong&gt; · And this allows you to change all of that at once without having to customize that on every screen that you use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:29&lt;/strong&gt; · It also provides a color scheme, which is the basis for these new Material 3 tokens and can provide those fonts directly into that ThemeData object, which is the root of all your themes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:39&lt;/strong&gt; · KHANH NGUYEN: All right, so let&apos;s go ahead and create an instance of that ThemeProvider and pass it to the scoped theme object in MaterialApp.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:47&lt;/strong&gt; · So let&apos;s go ahead and jump to lib/src/shared/app.dart and see what&apos;s up there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:53&lt;/strong&gt; · All right, so app.dart conveniently available here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:56&lt;/strong&gt; · All right, so this here says, &amp;quot;Create a theme instance.&amp;quot;&lt;/p&gt;
&lt;h3&gt;Create a Theme Instance&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;10:59&lt;/strong&gt; · Let&apos;s go back to our codelab and copy that code to create our theme instance here and plop it in right here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:10&lt;/strong&gt; · All right, and then for the next step, we&apos;ll jump to this line here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:14&lt;/strong&gt; · Go ahead and copy that again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:15&lt;/strong&gt; · So here, we&apos;re just passing it the light theme from our ThemeProvider.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:19&lt;/strong&gt; · So we can paste this here and save.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:25&lt;/strong&gt; · All right, and I&apos;m just going to jump over here and hot reload our app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:34&lt;/strong&gt; · I am just realizing now, Rody, we had never shown the new text, [LAUGHS] the new font that we updated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:42&lt;/strong&gt; · So let&apos;s jump back and take a look at that real fast while this is happening.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:48&lt;/strong&gt; · All right, so let&apos;s jump back over here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:52&lt;/strong&gt; · There you go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:53&lt;/strong&gt; · So the thing that we forgot to show you earlier was the &amp;quot;Good morning&amp;quot; here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:57&lt;/strong&gt; · So because we went from one sans-serif font, like Roboto, to Montserrat, it&apos;s not very super obvious, but if you look at the shape of the text itself, for example, like this G here in &amp;quot;Good morning,&amp;quot; you&apos;ll see that it&apos;s swapped now to the Montserrat font instead of the Roboto that we saw earlier-- just jumping quickly back to typography there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:18&lt;/strong&gt; · So next part that we were talking about was going to be the theme.&lt;/p&gt;
&lt;h3&gt;Theme&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;12:21&lt;/strong&gt; · So you&apos;ll see now here on the side&apos;s navigation rail is now a pink color instead of the Flutter blue that we saw before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:28&lt;/strong&gt; · It&apos;s just a little bit more subtle here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:30&lt;/strong&gt; · And so, as you can see on Playlist and the Artist page as well, we don&apos;t have the bright blue app bar any longer here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:38&lt;/strong&gt; · All right, so let&apos;s jump back to the codelab and take a look at our next step.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:43&lt;/strong&gt; · OK, so this next step here talks about the Material Theme Builder, which Rody was telling me about not too long ago, and it feels like, well, just yesterday, Rody.&lt;/p&gt;
&lt;h3&gt;The Material Theme Builder&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;12:51&lt;/strong&gt; · So why don&apos;t you tell us a little bit more about the Material Theme Builder here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:55&lt;/strong&gt; · RODY DAVIS: Of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:56&lt;/strong&gt; · Yeah, I&apos;m not a designer, and picking color schemes for my Flutter apps has always been a challenge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:01&lt;/strong&gt; · But, thankfully, we&apos;ve created a tool to help with this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:03&lt;/strong&gt; · It&apos;s called the Material Theme Builder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:05&lt;/strong&gt; · We can jump over and play around with some different colors that can be used as a source input for the new Material 3 algorithm, which will generate all of our colors, our light and our dark theme, based on that single source of truth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:17&lt;/strong&gt; · So once we do that, we can play around and see what looks good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:22&lt;/strong&gt; · You can override the different roles if you need to, but really, just need that one value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:28&lt;/strong&gt; · KHANH NGUYEN: So I get an entire theme for free just by giving it the primary color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:32&lt;/strong&gt; · RODY DAVIS: That&apos;s right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:34&lt;/strong&gt; · KHANH NGUYEN: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:35&lt;/strong&gt; · All right, so what color are we feeling today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:38&lt;/strong&gt; · RODY DAVIS: I think it may be like a blue or a green.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:42&lt;/strong&gt; · KHANH NGUYEN: All right, let&apos;s go-- how do you feel about seafoam green for today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:46&lt;/strong&gt; · RODY DAVIS: That looks great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:48&lt;/strong&gt; · KHANH NGUYEN: All right, let&apos;s take that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:49&lt;/strong&gt; · Look at that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:50&lt;/strong&gt; · That&apos;s a good color scheme, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:52&lt;/strong&gt; · RODY DAVIS: Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:54&lt;/strong&gt; · KHANH NGUYEN: All right, so let me go ahead and right-click on Primary and just copy that color value for that seafoam green right there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:02&lt;/strong&gt; · All right, so the next step is we need to add it to our ThemeProvider.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:07&lt;/strong&gt; · OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:08&lt;/strong&gt; · All right, let&apos;s jump back to VS Code, and we&apos;ll replace it in our app.dart file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:14&lt;/strong&gt; · So here, it says, &amp;quot;Replace this color.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:16&lt;/strong&gt; · We&apos;ll go ahead and replace it here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:20&lt;/strong&gt; · Hm, we need to replace that pound sign with 0xff to make it a hexadecimal value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:27&lt;/strong&gt; · Let&apos;s add a const while we&apos;re at it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:28&lt;/strong&gt; · RODY DAVIS: And also another thing to keep note of is, if you see the DynamicColorBuilder, this is a widget that we can use to grab the dynamic theme from the wallpaper when a user is running this on Android that is running the latest version.&lt;/p&gt;
&lt;h3&gt;Dynamic Color Builder&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;14:43&lt;/strong&gt; · What&apos;s really cool about this is they can pick the colors from the wallpaper, and this theme will be now reflected in your applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:49&lt;/strong&gt; · But Flutter runs on all the other platforms, so we&apos;ll need to fall back to this custom theme that we just defined.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:55&lt;/strong&gt; · KHANH NGUYEN: Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:55&lt;/strong&gt; · Actually, this will need a hot restart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:04&lt;/strong&gt; · All right, there you go, performing a hot restart, and let&apos;s jump over and take a look.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:16&lt;/strong&gt; · The we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:17&lt;/strong&gt; · So now we see the navigation rail is using that nice seafoam green right there that we talked about earlier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:25&lt;/strong&gt; · Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:25&lt;/strong&gt; · All right, and so, so far, Rody, you&apos;ve mentioned this color scheme, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:31&lt;/strong&gt; · But so far, I&apos;m only seeing this seafoam green that we picked out in the Material Theme Builder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:37&lt;/strong&gt; · And here, the next step in the codelab is telling us to add a box decoration to our OutlinedCard.&lt;/p&gt;
&lt;h3&gt;Box Decoration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;15:43&lt;/strong&gt; · And I can see that, for the color, it&apos;s getting the colorScheme.outline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:48&lt;/strong&gt; · What are these different color roles that are available with the color scheme?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:53&lt;/strong&gt; · RODY DAVIS: Of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:54&lt;/strong&gt; · Yes, so the Material 3 system now includes these new color tokens that you can use for your applications, such as Primary, Secondary, and Tertiary, and Container colors and On Container colors.&lt;/p&gt;
&lt;h3&gt;Color Tokens&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;16:07&lt;/strong&gt; · And these are values that you can build with that are the same on light and dark theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:13&lt;/strong&gt; · These values are meant to change at runtime, but give you the flexibility of choosing things that are going to be accessible, but where you don&apos;t know the value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:23&lt;/strong&gt; · And that lets you update your application and change your color without having to update any of the widgets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:29&lt;/strong&gt; · KHANH NGUYEN: Gotcha, OK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:30&lt;/strong&gt; · So I&apos;m not checking if it&apos;s Dark mode or if it&apos;s Light mode and then returning a different color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:34&lt;/strong&gt; · I&apos;m just telling you, OK, just use the outline color, whatever that is, based on the light theme or the dark theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:40&lt;/strong&gt; · That&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:41&lt;/strong&gt; · I love that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:42&lt;/strong&gt; · [LAUGHS] All right, let&apos;s go ahead and add the box decoration to our OutlinedCard, which is available in lib/src/shared/views/ and outlined_card.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:52&lt;/strong&gt; · So let&apos;s jump over to our VS Code here, and let&apos;s scroll down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:58&lt;/strong&gt; · All right, so there&apos;s a nice comment here that tells us exactly where to add our BoxDecoration() code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:03&lt;/strong&gt; · And let&apos;s go ahead and save that and do a hot reload.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:08&lt;/strong&gt; · All right, and jump over to myartist again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:10&lt;/strong&gt; · So now you&apos;ll see that the app is now-- these tiles in the app have an outline on each of the cards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:19&lt;/strong&gt; · So this is going to be in the color that is the outline color from our color scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:25&lt;/strong&gt; · So that&apos;s really nice because it adds just a little bit more separation between the cards themselves and the background as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:33&lt;/strong&gt; · Neat.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:33&lt;/strong&gt; · All right, let&apos;s jump back here and scroll down a little bit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:37&lt;/strong&gt; · So we keep talking about this dark theme, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:40&lt;/strong&gt; · We don&apos;t even have a dark theme, so why worry about it, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:44&lt;/strong&gt; · But, whoa, wait a second here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:46&lt;/strong&gt; · What is this?&lt;/p&gt;
&lt;h3&gt;Adding a Dark Theme&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;17:48&lt;/strong&gt; · Oh, we&apos;re adding a dark theme to our app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:50&lt;/strong&gt; · How do we just add a dark theme to our app?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:52&lt;/strong&gt; · RODY DAVIS: Yeah, so thankfully, the ThemeProvider already takes care of the Dark theme for us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:56&lt;/strong&gt; · We just need to add it to our MaterialApp.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:58&lt;/strong&gt; · Important note is this Dark theme is generated from that same color, so it&apos;s not just a monochrome scheme, and it&apos;ll look really great on both light and dark theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:07&lt;/strong&gt; · In addition, we&apos;re also checking to see if the user has Dark mode selected on their system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:12&lt;/strong&gt; · And if they are, we&apos;re going to go ahead and show it, but allow them to change it at runtime if needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:16&lt;/strong&gt; · KHANH NGUYEN: All right, that&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:18&lt;/strong&gt; · So if we scroll down here, there&apos;s a nice comment again, two nice comments, to tell us exactly where to add the Dark theme and adding the themeMode to be passed through to the MaterialApp.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:29&lt;/strong&gt; · All right, so I saved that there in app.dart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:32&lt;/strong&gt; · Let&apos;s go ahead and hot reload again and jump over to myartist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:43&lt;/strong&gt; · All right, so like Rody mentioned before, now we have a Dark theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:47&lt;/strong&gt; · But we&apos;re basing it based on system settings right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:52&lt;/strong&gt; · So I have Light theme enabled for my computer, but I can manually enable Dark mode for my myartist app by clicking this moon up here, and there you go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:03&lt;/strong&gt; · You see now we have myartist in a Dark theme mode, but it&apos;s still using that same seafoam green that we picked out earlier to seed the theme with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:13&lt;/strong&gt; · So that&apos;s super awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:15&lt;/strong&gt; · That&apos;s super nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:16&lt;/strong&gt; · I loved it when Rody introduced me to it just because, usually, when I pick colors for my apps, I&apos;ll pick the first color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:22&lt;/strong&gt; · I&apos;ll know what this first color is, but then the secondary color, the tertiary color-- I tend to go by the philosophy of, I&apos;ll pick a color and hope that it works out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:33&lt;/strong&gt; · So having the Material Theme Builder to help me get to a nice color scheme was super useful, and I&apos;m super excited about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:41&lt;/strong&gt; · So that&apos;s it for theming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:43&lt;/strong&gt; · Let&apos;s go ahead and jump over to the next step of the workshop, which is making the app adaptive.&lt;/p&gt;
&lt;h3&gt;Making the App Adaptive&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;19:49&lt;/strong&gt; · Rody, want to take us away?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:50&lt;/strong&gt; · RODY DAVIS: Yep, so when building with Flutter, you&apos;re going to come across a lot of new platforms that you may not be familiar with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:56&lt;/strong&gt; · For example, when targeting desktop, this is a keyboard-and-mouse-only kind of mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:02&lt;/strong&gt; · There are some form factors out there that even support touch, but then your app can be running on mobile, where it&apos;s only going to be touch and gestures, and sometimes a keyboard, as well as maybe a TV or some other kind of form factor that&apos;s a gamepad.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:16&lt;/strong&gt; · And so, when building with this, you&apos;re going to-- adaptive means taking these different forms of input as stylistic choices when you&apos;re building for your widgets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:26&lt;/strong&gt; · You can adapt each widget based on not only the screen resolution but the type of input that you&apos;re going to be receiving.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:34&lt;/strong&gt; · So one of the first things that we can do is actually change the navigation to help the user change targets a little bit easier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:42&lt;/strong&gt; · When building with a bottom navigation, it allows the user to tap it with their thumb as opposed hard-to-reach places like the navigation rail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:50&lt;/strong&gt; · KHANH NGUYEN: All right, so let&apos;s go ahead and make that change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:53&lt;/strong&gt; · So like Rody mentioned, let&apos;s go ahead and add the mobile layout for our bottomNavigationBar.&lt;/p&gt;
&lt;h3&gt;Bottom Navigation Bar&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;21:01&lt;/strong&gt; · And we can do that in lib/src/shared/views/adaptive navigation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:05&lt;/strong&gt; · And, conveniently, we have that in our VS Code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:09&lt;/strong&gt; · So let&apos;s go ahead and look at adaptive_navigation here.&lt;/p&gt;
&lt;h3&gt;Adaptive Navigation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;21:12&lt;/strong&gt; · And here, there&apos;s a comment that tells us exactly where to add the bottomNavigationBar code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:19&lt;/strong&gt; · However, we&apos;re never going to get to that code because, well, we&apos;re returning the Scaffold at the top.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:24&lt;/strong&gt; · So let&apos;s add the conditional check to make sure that we return the Tablet layout only when it&apos;s actually tablet size or larger.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:33&lt;/strong&gt; · Otherwise, we&apos;re going to return the bottomNavigationBar for mobile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:38&lt;/strong&gt; · And so we can add that here by checking the dimensions that&apos;s being passed through to the LayoutBuilder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:44&lt;/strong&gt; · So we&apos;ll go here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:47&lt;/strong&gt; · We&apos;ll add this if statement up top and then add a closing bracket.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:52&lt;/strong&gt; · All right, there we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:54&lt;/strong&gt; · We save this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:56&lt;/strong&gt; · And, whoop, we did not want to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:58&lt;/strong&gt; · We do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:04&lt;/strong&gt; · All right, it&apos;s hot reloaded, and we&apos;ll jump back over to myartist, and voila, you see now there&apos;s a bottom navigation bar on mobile when you&apos;re in mobile size.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:13&lt;/strong&gt; · But if you scale it up a little bit more, we get back to the navigation rail that&apos;s still there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:19&lt;/strong&gt; · So adding this navigation bar at the bottom now just makes it a little bit easier to access the various navigation menus when you&apos;re on mobile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:28&lt;/strong&gt; · All right, and jumping back to our codelab, we&apos;ll go down a little bit more here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:32&lt;/strong&gt; · And, Rody, do you want to talk a little bit more about the layout for the home screen and how we can improve that a little bit more?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:40&lt;/strong&gt; · RODY DAVIS: Yeah, so when building with Responsive, you need to operate off of things like a column grid, as well as a set of break points.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:49&lt;/strong&gt; · We have added these here on the extension method under the BoxConstraint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:54&lt;/strong&gt; · So we can check to see if we&apos;re on these different devices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:56&lt;/strong&gt; · Now, on the home screen, there&apos;s different things that we can do to take advantage of the screen real estate, such as we can use a DataTable versus a ListView on tablets and desktops, where the user is operating it with a mouse, and they want to see more dense information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:13&lt;/strong&gt; · Another thing that we can do on the home screen is actually change this content into a TabBar and a TabBarView.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:18&lt;/strong&gt; · Now, there&apos;s many reasons to do this, but one of them being that the content is related, and we can let the user just not overload them with a bunch of information all at once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:28&lt;/strong&gt; · And this also is easy for them to change between the content by swiping between it with their thumb.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:34&lt;/strong&gt; · KHANH NGUYEN: OK, so the next step in the codelab seems to be breaking up that content into the TabBarView by checking if it&apos;s a mobile device.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:42&lt;/strong&gt; · And if it is, it returns that TabBarView that Rody just mentioned.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:45&lt;/strong&gt; · So we&apos;ll go ahead and add that TabBarView to our home screen by copying this chunk of text here within that if constraints.isMobile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:54&lt;/strong&gt; · And we&apos;ll go ahead back into our VS Code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:59&lt;/strong&gt; · Let&apos;s X out of adaptive_navigation here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:01&lt;/strong&gt; · Whoop, let&apos;s make sure I save this to make sure I have everything I want there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:06&lt;/strong&gt; · Let&apos;s get out there and jump over to home_screen.dart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:11&lt;/strong&gt; · So that will be lib/src/features/home/view/home screen.dart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:16&lt;/strong&gt; · And wow, just have that conveniently open.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:19&lt;/strong&gt; · So we&apos;ll go ahead and add that at the very top here of the build() method, which says, &amp;quot;Add conditional mobile layout.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:27&lt;/strong&gt; · So easily just paste that in there, and we&apos;ll go and hot reload.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:37&lt;/strong&gt; · All right, that&apos;s running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:39&lt;/strong&gt; · While we do that, let me jump over here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:42&lt;/strong&gt; · And so there we are.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:44&lt;/strong&gt; · So you can see that layout just quickly changed from that layout we had on mobile but just squeezed together in a smaller screen, now to the TabBarView, which breaks all of the content on that home screen now into a separate tab so that it doesn&apos;t feel as scrunched up into one screen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:02&lt;/strong&gt; · So that looks a lot better already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:05&lt;/strong&gt; · All right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:06&lt;/strong&gt; · Whoop, I did not mean to open that directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:09&lt;/strong&gt; · All right, OK, cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:11&lt;/strong&gt; · So now that we have the mobile layout on mobile devices, we&apos;ll want to jump back to the desktop and make a few modifications there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:22&lt;/strong&gt; · So if I go back to desktop-- there we go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:26&lt;/strong&gt; · We want to open myartist again and scale that open to, say, desktop size.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:32&lt;/strong&gt; · All right, that&apos;s what we want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:34&lt;/strong&gt; · It looks a bit better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:35&lt;/strong&gt; · All right, so from here, let&apos;s talk about white space.&lt;/p&gt;
&lt;h3&gt;White Space&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;25:40&lt;/strong&gt; · So with white space, it&apos;s an important visual tool to use for your app, using it as an organizational tool to break up sections and pieces of content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:51&lt;/strong&gt; · So, personally, I find that it&apos;s better to have too much white space than not enough because when I add too much white space, I notice that it&apos;s too much white space and that I will have to remove white space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:04&lt;/strong&gt; · However, when I don&apos;t have enough white space, I have a tendency of saying, oh, no, I think that looks fine, or I&apos;ll reduce the size of the font, or reducing the size of an image when I really should be doing that for readability and just for visibility purposes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:19&lt;/strong&gt; · And so, for me, the process with adding white space is, I&apos;ll go too much.&lt;/p&gt;
&lt;h3&gt;Adding White Space&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;26:23&lt;/strong&gt; · I&apos;ll add too much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:24&lt;/strong&gt; · I&apos;ll be very heavy-handed with adding my white space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:26&lt;/strong&gt; · And then we&apos;ll go back and iteratively tweak that a little bit more to our liking so that there&apos;s just the right amount of white space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:34&lt;/strong&gt; · And so here in the codelab, there&apos;s a great example with myartist of not having enough white space and having too much white space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:40&lt;/strong&gt; · The too much white space, I think, is a little bit more obvious in that there&apos;s way too much white space, whereas the first image is-- well, it&apos;s tolerable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:49&lt;/strong&gt; · There&apos;s not a great amount of white space, but it&apos;s fine, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:53&lt;/strong&gt; · So let&apos;s go ahead and update the padding on our home screen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:57&lt;/strong&gt; · So there is a few lines here that tell us to modify the padding values to 35.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:02&lt;/strong&gt; · So let&apos;s go ahead and do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:05&lt;/strong&gt; · And while I know I could probably just go through and do a find and replace for all of the EdgeInsets.all() value, I&apos;m going to go through it and update it manually because I find that this keeps me aware of where I&apos;m changing the values, and it gives me a better idea of what to iterate-- what number I should use next when I&apos;m going back and forth and trying to find the right amount of white space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:28&lt;/strong&gt; · So we&apos;ll update this to 35, maybe this 35 as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:33&lt;/strong&gt; · Let&apos;s update this here and here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:37&lt;/strong&gt; · And that should be it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:39&lt;/strong&gt; · That is all that we&apos;re going to update for now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:42&lt;/strong&gt; · So, with that, we&apos;ll save this file and go ahead and hot reload.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:50&lt;/strong&gt; · All right, and so we&apos;ll jump back to myartist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:54&lt;/strong&gt; · And you can see that white space just changed there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:56&lt;/strong&gt; · There&apos;s a lot more space available now, a lot of room to breathe for all the content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:01&lt;/strong&gt; · But this banner at the top here still does not have enough padding on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:06&lt;/strong&gt; · It does not have enough white space around it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:08&lt;/strong&gt; · So let&apos;s go ahead and fix that as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:11&lt;/strong&gt; · And that just happens to be the next step of this codelab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:14&lt;/strong&gt; · So we&apos;ll jump into the lib/src/features/home/view/home highlight and update that to be a 35 padding as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:23&lt;/strong&gt; · So here, we&apos;ll go to home_highlight and change this to a 35.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:31&lt;/strong&gt; · All right, we&apos;ll save that and hot reload again, then jump back into the app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:35&lt;/strong&gt; · OK, there&apos;s a little bit more white space now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:39&lt;/strong&gt; · All right, and you can see the edges came in just a tad bit more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:43&lt;/strong&gt; · All right, and so if we jump over here, let&apos;s look at the next step.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:48&lt;/strong&gt; · So this here calls out the two playlists at the bottom.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:51&lt;/strong&gt; · Let&apos;s look at the live app and take a look here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:54&lt;/strong&gt; · So these two tables don&apos;t necessarily have anything in common.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:58&lt;/strong&gt; · They&apos;re two separate entities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:00&lt;/strong&gt; · But, when you look at this table, you think, oh, these two tables are related because, well, they&apos;re connected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:05&lt;/strong&gt; · They&apos;re one table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:06&lt;/strong&gt; · So what&apos;s the relation?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:07&lt;/strong&gt; · An easy solution here is to just add some space in between so that we can separate the entities that they were meant to be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:14&lt;/strong&gt; · So if the Top Songs Today was separate from New Releases, people wouldn&apos;t be thinking, hey, what do these playlists have in common?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:21&lt;/strong&gt; · So with that, the next step is just add a SizedBox() in between those two playlists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:28&lt;/strong&gt; · So let&apos;s go ahead and jump over here and add the SizedBox() in home_screen.dart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:36&lt;/strong&gt; · So if we scroll down a little bit here, we can add that SizedBox().&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:40&lt;/strong&gt; · But, Rody, do you want to tell everyone why we are using a SizedBox() here instead of, say, a Container?&lt;/p&gt;
&lt;h3&gt;Why We Are Using a Size Box&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;29:45&lt;/strong&gt; · RODY DAVIS: Yeah, so you want to use a SizedBox() anytime you&apos;re only changing layout because a Container can actually have a performance penalty if you&apos;re just using it as your default. And there&apos;s a lot of other things that can use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:58&lt;/strong&gt; · Like, you can just use padding as opposed to adding padding into Container and a bunch of other animations and decorations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:03&lt;/strong&gt; · You can use DecoratedBox() and so on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:05&lt;/strong&gt; · So try to use SizedBox() when possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:07&lt;/strong&gt; · KHANH NGUYEN: Exactly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:08&lt;/strong&gt; · All right, so here, we jump back into the playlists, and we see now there&apos;s just a little bit of space, and they both look like they&apos;re separate tables now as opposed to one connected table that could have some relation to each other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:23&lt;/strong&gt; · Right there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:24&lt;/strong&gt; · All right, and let&apos;s go to the next step in our codelab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:29&lt;/strong&gt; · All right, so from here, we&apos;re going to continue refining our white space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:33&lt;/strong&gt; · So we talked about earlier that we have a lot of space to breathe, but this is probably too much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:39&lt;/strong&gt; · So you can fine-tune those padding values a little bit more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:42&lt;/strong&gt; · So while I go ahead and input the new padding values that we want here, Rody, can you share a little bit about the different EdgeInsets methods that you can use?&lt;/p&gt;
&lt;h3&gt;Edge and Sets Methods&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;30:51&lt;/strong&gt; · RODY DAVIS: Yeah, of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:52&lt;/strong&gt; · So there&apos;s a bunch of new EdgeInsets things that you can use that are constructor values.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:59&lt;/strong&gt; · And these are part ones that you can apply to set it differently and asymmetrically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:06&lt;/strong&gt; · So first off, you got left, right, top, bottom, which does exactly that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:10&lt;/strong&gt; · It lets you apply it in that order.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:12&lt;/strong&gt; · There&apos;s also symmetric(), which allows you to apply padding just on the vertical or horizontal part of it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:18&lt;/strong&gt; · And then we also have only(), which lets you choose a specific side and add that padding, which is useful for just nudging it a little bit into the place that you want it to be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:28&lt;/strong&gt; · So, especially when you&apos;re building with a 12-column grid, this allows you to align those things just right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:35&lt;/strong&gt; · KHANH NGUYEN: All right, so Rody was just a tad faster than me there, so I&apos;m still making these changes here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:39&lt;/strong&gt; · So let&apos;s go ahead and put in the final changes.&lt;/p&gt;
&lt;h3&gt;Final Changes&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;31:43&lt;/strong&gt; · So these last two, Top Songs Today and New Releases, will have the same EdgeInsets value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:50&lt;/strong&gt; · So I&apos;ll save this here, and we should be good to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:53&lt;/strong&gt; · So let&apos;s go ahead, and we&apos;ll hot reload again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:58&lt;/strong&gt; · All right, and jump over to app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:01&lt;/strong&gt; · All right, that&apos;s looking a lot better already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:04&lt;/strong&gt; · There is some white space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:05&lt;/strong&gt; · There&apos;s room to breathe, but not too much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:07&lt;/strong&gt; · But right now, there&apos;s still a little bit too much white space on the banner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:11&lt;/strong&gt; · So, of course, conveniently, that&apos;s also the next step of the codelab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:15&lt;/strong&gt; · So we&apos;re just to update the home_highlights.dart padding again just one more time to get it just where we want it to be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:24&lt;/strong&gt; · So jumping back to home_highlight, we&apos;ll replace this file, this padding value here, and jump over to-- actually, we&apos;re going to want to hot reload and jump over to [INAUDIBLE].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:40&lt;/strong&gt; · There you go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:41&lt;/strong&gt; · All right, that&apos;s looking a lot better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:43&lt;/strong&gt; · We added more white space so that there&apos;s room to breathe, but everything looks a lot more cohesive now than before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:51&lt;/strong&gt; · All right, so let&apos;s jump over to Chrome again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:54&lt;/strong&gt; · All right, and so that&apos;s it for adaptive design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:59&lt;/strong&gt; · Lastly, we want to talk about motion and animation.&lt;/p&gt;
&lt;h3&gt;Motion and Animation&lt;/h3&gt;
&lt;h3&gt;Animating between Screens&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;33:03&lt;/strong&gt; · Rody, do you want to talk about animating between screens?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:05&lt;/strong&gt; · RODY DAVIS: Yeah, of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:07&lt;/strong&gt; · So, on desktop, users expect things to be a little bit more precise and snappy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:12&lt;/strong&gt; · So one of the ways that we can help with this is taking out the animation completely when you&apos;re on Linux, macOS, or Windows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:19&lt;/strong&gt; · We want to keep those animations for mobile, however, because it provides that sense that something is happening, and the users are expecting it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:27&lt;/strong&gt; · It may also cause jank if everything is just jumping around, and they don&apos;t know why it&apos;s happening.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:32&lt;/strong&gt; · So you can go ahead and add those into the ThemeProvider and apply it easily.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:36&lt;/strong&gt; · KHANH NGUYEN: Awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:37&lt;/strong&gt; · So the pageTransitionsTheme here, where we define the transition for Android, iOS, Linux, macOS, and Windows-- that&apos;s already provided in the theme.dart file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:48&lt;/strong&gt; · So all you have to do is use it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:49&lt;/strong&gt; · And you can use it by adding just these two lines here, passing in the pageTransitionsTheme to the Light and Dark theme, as Rody mentioned.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:58&lt;/strong&gt; · So let&apos;s jump into VS Code and make those changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:01&lt;/strong&gt; · So if we go to lib/src/shared/providers/ and theme.dart, we can add the page transitions here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:12&lt;/strong&gt; · All right, I will make sure we add a comma here and at the top.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:18&lt;/strong&gt; · RODY DAVIS: So how does this look on a simulator?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:20&lt;/strong&gt; · KHANH NGUYEN: So how does it look on a simulator?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:22&lt;/strong&gt; · That&apos;s a good question, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:23&lt;/strong&gt; · So there are screenshots.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:25&lt;/strong&gt; · Well, not screenshots.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:26&lt;/strong&gt; · There are screencasts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:27&lt;/strong&gt; · There&apos;s GIFs right here, which show us exactly what it looks like.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:30&lt;/strong&gt; · So you can see, with that animation on iOS, it looks very choppy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:35&lt;/strong&gt; · It looks like there&apos;s a delay when the screen is loading, whereas with the animation, it looks very smooth and looks native to mobile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:44&lt;/strong&gt; · So that&apos;s exactly what it looks like, whereas on desktop, as we can see in myartist right here, there is no transition, and that looks much better on desktop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:54&lt;/strong&gt; · And it&apos;s much more of what users expect for a desktop app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:00&lt;/strong&gt; · Cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:01&lt;/strong&gt; · And last but not least are hover states.&lt;/p&gt;
&lt;h3&gt;Hover States&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;35:03&lt;/strong&gt; · Rody, why do we want hover states?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:06&lt;/strong&gt; · And what&apos;s so important about it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:09&lt;/strong&gt; · RODY DAVIS: Yeah, so when you&apos;re on desktop, one of the things that you want to do is provide additional feedback that you don&apos;t get on other devices where it&apos;s touch only.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:18&lt;/strong&gt; · For example, when you&apos;re hovering over something, if something&apos;s clickable, you want to maybe change the border radius or the color to just provide that extra feedback to show that you can click on something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:29&lt;/strong&gt; · And then, in addition, you can even change the mouse cursor on desktop that you can&apos;t on mobile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:35&lt;/strong&gt; · KHANH NGUYEN: All right, so now we&apos;re going to add the hover state to our OutlinedCard.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:41&lt;/strong&gt; · And here, for the codelab instructions-- tells us we&apos;re just going to replace the entire OutlinedCard code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:46&lt;/strong&gt; · So I&apos;ll go ahead and copy this huge chunk of code here and go ahead back into VS Code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:53&lt;/strong&gt; · If you go into lib/src/shared/views, and then we jump into outlined_card.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:01&lt;/strong&gt; · We were just in this file a few steps ago, so we can just select all, delete that, and paste this here, and save.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:11&lt;/strong&gt; · All right, and we&apos;ll go ahead and hot reload that and jump back into myartist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:19&lt;/strong&gt; · All right, so once that&apos;s hot reloaded, you&apos;ll see just there, the corners now are a little bit more rounded, but when you hover over top of it, the opacity animates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:27&lt;/strong&gt; · It changes, as well as the border radius on the cards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:31&lt;/strong&gt; · So that gives users just a little bit more feedback when they&apos;re hovering over things that are clickable, like these cards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:38&lt;/strong&gt; · All right, and so jumping back to the codelab, we have just one more step to go here, and that is-- well, this entire time, we&apos;ve been talking about a music app, but we haven&apos;t played any songs because we haven&apos;t come across any Play button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:53&lt;/strong&gt; · So what we could do here is add a hoverableSongPlayButton widget that we&apos;ve defined for this app in particular.&lt;/p&gt;
&lt;h3&gt;Hoverable Song Play Button Widget&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;37:01&lt;/strong&gt; · So what we&apos;ll do is we&apos;ll add this hoverableSongPlayButton widget.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:05&lt;/strong&gt; · We&apos;ll wrap it around our song rankings for our playlist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:10&lt;/strong&gt; · So that way, when you hover over the numbers, the number-- that animates into a Play button that you can hit play on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:16&lt;/strong&gt; · So I&apos;ll copy this chunk of text here and jump into playlists and playlist_songs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:23&lt;/strong&gt; · So let&apos;s give the VS Code a moment here and jump into playlist_songs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:28&lt;/strong&gt; · So that&apos;s under lib/src/features/playlists/view/ and playlist_songs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:34&lt;/strong&gt; · So here, there is a nice, little comment to tell us that we want to wrap it in the hoverableSongPlayButton.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:40&lt;/strong&gt; · Don&apos;t forget to close the parentheses here and save.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:46&lt;/strong&gt; · All right, and so we&apos;ll jump down here, give it a hot reload, and check out the app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:55&lt;/strong&gt; · All right, so it hot reloaded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:56&lt;/strong&gt; · And so now, when we hover over the top songs, you&apos;ll see it animates into the blue-- into the Play button here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:04&lt;/strong&gt; · And here, you&apos;ll see the bottom bar is a blue color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:09&lt;/strong&gt; · And so that is a tertiary color that will go along with the seafoam green that we had picked earlier but so that the entire app is not just the green color that we picked for the primary value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:22&lt;/strong&gt; · So with the Material Theme Builder and the sea color, the color scheme that comes back-- you&apos;ll get a variety of colors so that you&apos;re not stuck with just your primary theme color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:33&lt;/strong&gt; · All right, and so that just about does it, and that brings us to the end of this codelab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:40&lt;/strong&gt; · And that&apos;s it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:41&lt;/strong&gt; · That takes us to the end of our workshop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:43&lt;/strong&gt; · Rody, what did we do today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:45&lt;/strong&gt; · RODY DAVIS: So today, if you remember, when we started, we had a very generic app that had the blue-on-blue theme and the Roboto font.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:52&lt;/strong&gt; · But by using a Montserrat, a custom theme that we pull from Google Fonts, we were able to make our typography feel very personal to our brand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:59&lt;/strong&gt; · We picked out a color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:00&lt;/strong&gt; · We&apos;re using the Material Theme Builder, and we&apos;re able to generate a Light and a Dark theme that looks beautiful in both contexts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:06&lt;/strong&gt; · We made our app adaptive for mouse and touch input, so it feels good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:10&lt;/strong&gt; · And we made it responsive on all the break points that Flutter is going to be running, as well as adding white space for making it dense and comfortable in the various modes, and motion and animation for those little bit of extra-polish features that the users expect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:24&lt;/strong&gt; · KHANH NGUYEN: Even with everything that we&apos;ve just touched on, as Rody just mentioned, this is really just a starting point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:30&lt;/strong&gt; · Design can vary in everything specific to a particular project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:33&lt;/strong&gt; · Hopefully, you&apos;re able to take some of these tips and tricks and implement it into your own app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:38&lt;/strong&gt; · So it&apos;s our first codelab, and it was a lot of fun to work on, but we&apos;d like to know from you, how can we improve on this codelab?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:44&lt;/strong&gt; · Do you have design tricks that you like integrating into your own apps?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:48&lt;/strong&gt; · Please do let us know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:49&lt;/strong&gt; · If you&apos;re interested in learning more, check out the next steps and related content on the codelab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:53&lt;/strong&gt; · As always, Flutter.dev and Material.io will be the place to find more information on Flutter and Material.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:59&lt;/strong&gt; · So thanks for joining us on our journey in making this app beautiful, and we&apos;ll see you next time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:04&lt;/strong&gt; · Bye.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:04&lt;/strong&gt; · RODY DAVIS: Bye.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:05&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Flutter Plugin - Live Coding - Native Color Picker</title><link>https://rodydavis.com/videos/flutter/flutter-plugin-live-coding-native-color-picker/</link><guid isPermaLink="true">https://rodydavis.com/videos/flutter/flutter-plugin-live-coding-native-color-picker/</guid><description>A video tutorial demonstrates the process of creating a native color picker plugin for Flutter, focusing initially on web implementation while setting up the necessary package structure for future mobile expansion.</description><pubDate>Wed, 01 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Flutter Plugin - Live Coding - Native Color Picker&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;zY3mWKifRd8&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;In this video I walk through my debugging and plugin creation process!&lt;/p&gt;
&lt;p&gt;Source: https://github.com/rodydavis/native_color_picker&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · hey guys my name is ready today we&apos;re gonna be building a native color picker for the web maybe later on we&apos;re gonna add iOS and Android but at least right now we&apos;re gonna focus on the web so first of all what you&apos;re gonna go ahead and do is create a folder and then open up that folder in vs code let&apos;s go ahead and create the default package so flutter create - package and then we&apos;re&lt;/p&gt;
&lt;h3&gt;Create the Default Package&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · just use dot I so as we know we&apos;re already in the folder let&apos;s also create the example perfect now to get this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:50&lt;/strong&gt; · registered let&apos;s go ahead and reload the window once this finishes and let&apos;s go ahead and remove all the comments if you&apos;re curious about some of the extensions I&apos;ll be using I have removed all comments right here and we also have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · I&apos;ll be using remove blank lines right here as well as the flutter plug-in so here we have this this is deprecated so we can remove this let&apos;s go ahead and add our URL&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · and our SDK level is good you can go ahead and update the description&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:07&lt;/strong&gt; · perfect and let&apos;s go in and remove this of test for now so what we&apos;re gonna do first is we&apos;re going to be creating a federated plugin that only works on the web at the moment so let&apos;s create a source folder and a native color picker&lt;/p&gt;
&lt;h3&gt;Creating a Federated Plugin&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:43&lt;/strong&gt; · let&apos;s go ahead and export that this is useful for the way dart deals with files and we can also give it a version that people care about it okay&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:12&lt;/strong&gt; · now we want to have a couple different files in here we&apos;re gonna have a web dart a impl dot dart and you will add a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:33&lt;/strong&gt; · a mobile tart and a desktop pop tart since all these could be done in different ways and it should be it so now this file is going to use conditional importing and we actually need to add a unsupported&lt;/p&gt;
&lt;h3&gt;Conditional Importing&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:05&lt;/strong&gt; · this will be our fallback on the puffiness we don&apos;t support so to start off since we&apos;re only supporting and actually we can remove this I can put this here this will make sense in a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:22&lt;/strong&gt; · minute so we can go and delete that all right so we have these files right here we&apos;re gonna start off with the impl because this will allow us to implementation as well short for it but it allows us to define our interface at the other classes are going to override so we&apos;re going to do abstract class let&apos;s call it color thicker feel perfect&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:50&lt;/strong&gt; · and then we&apos;re gonna have a let&apos;s just give it a generic class of a function I mean sorry color this method will be let&apos;s do pick color and we can update&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:11&lt;/strong&gt; · this as we go to so one of the nice things about this is that it&apos;s flexible okay perfect actually we want to declare this without a function body since we want it to be overridden okay so let&apos;s go over to web this will be the one we start on recall color let&apos;s do native color picker native color picker extends color pickle on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:45&lt;/strong&gt; · implementation let&apos;s go ahead and import that and we see we need to override it so in this case we&apos;re also going to be importing HTML we can do this because we don&apos;t have to worry about dart i/o or anything else in the native side since we&apos;re conditionally importing it right here so let&apos;s look at this API so this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:16&lt;/strong&gt; · is the API that I found what&apos;s cool about it is it offers a native functionality so like if you click a color on Chrome for example it&apos;ll pop up the Mac OS color picker which is super cool giving you also the option for an eyedropper and then on Safari and mobile&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:35&lt;/strong&gt; · it&apos;ll even have a special array type and then you can click show colors and also show this too I just thought this was super neat and it even shows a special keyboard on mobile so something the browser supports so we need to look up this API so we have a lot of the ways I find the documentation for some of this stuff for dart and the web is from angular dart and dart just being used all the web so we can try searching for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:09&lt;/strong&gt; · actually we need elements so let&apos;s go ahead and and look at this and see what it provides&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:30&lt;/strong&gt; · okay you got this is some of the default ones I&apos;ve used the file upload one before let&apos;s see if it&apos;s already defined so let&apos;s go back to our web and we can go ahead and close these out so let&apos;s see what we got color and put element let&apos;s see we have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:15&lt;/strong&gt; · a couple different types but we need specifically the input one so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:35&lt;/strong&gt; · it&apos;s actually open back up the API documentation so it&apos;s type of color you can give it a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:53&lt;/strong&gt; · name let&apos;s also give it a value so this will be you can also pass in that default value so let&apos;s go ahead and add this this we need to put it into a hexadecimal form and I&apos;ll show you guys how to do that so let&apos;s go back to here I&apos;m going to do color color default or&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:19&lt;/strong&gt; · let&apos;s do value this way if the colors user selects a color the next time they go to select it though so it&apos;ll pre fill it with that correct color so let&apos;s go ahead and add nothing here you can do right here if it&apos;s not equal to null you&apos;re doing I&apos;ll go duplicate it all right let&apos;s see we can do let&apos;s see how&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:57&lt;/strong&gt; · we can convert to hexadecimal together pulled up three here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:24&lt;/strong&gt; · I have this function let&apos;s go ahead and drop it in we need color package so let&apos;s go ahead and then pour top&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:49&lt;/strong&gt; · so we&apos;re curious let&apos;s go ahead and import this here it&apos;s around packages get awesome so now this will take our color value and return a hexadecimal CSS string which is what we need so here C&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:15&lt;/strong&gt; · does that we&apos;re gonna put the pound sign for us see what provides yes it does provide it perfect so we can do color implementation cutter string and can put our color right there awesome sweet so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:42&lt;/strong&gt; · that should be good to go now we just need to return the color once they select it so let&apos;s first of all just test to see if this works let&apos;s open up error example do the same thing as earlier remove all comments you can format it lets depend on our current package so you select it is just saying&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:08&lt;/strong&gt; · to look up one root look up one directory so we can import it we&apos;re gonna have about there so we&apos;re just testing this out let&apos;s do let&apos;s just make the increment counter call this function so you called native color picker&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:38&lt;/strong&gt; · make sure it&apos;s imported current packages kit oh we need a sorry we need to add one more thing let&apos;s go down to unsupported we always have to have this just do class here extends just like before whoops Collin in here we&apos;re gonna do something&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:11&lt;/strong&gt; · a little bit different but it&apos;s still fine import it and override perfect if you just hover i&apos;m unimplemented error you can say not supported and let&apos;s go back here subscribe this perfect that&apos;s what we wanted and you change this to a a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:50&lt;/strong&gt; · singleton so it looks a little bit better you can say here this is just for example but see right now it&apos;s just grabbing it let&apos;s go ahead and run it on the web we can also create a launch file&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:16&lt;/strong&gt; · just for simplicity okay perfect let&apos;s go ahead and move this to the left it&apos;s a cool application by the way it&apos;s called Divi allows you to kind of put things side by side this is a really awesome API suite so we can also do some autocomplete as well it&apos;s pretty good listing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:51&lt;/strong&gt; · we&apos;ll also need this is where we&apos;re going to do next is we&apos;re going to have the the callbacks for the color awesome okay let&apos;s see what happens if everything goes well let&apos;s just show the native color picker&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:28&lt;/strong&gt; · and did not do we either provide a color to begin with let&apos;s see let&apos;s try giving it a face one so stop let&apos;s do red&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:57&lt;/strong&gt; · interesting let&apos;s check the console let&apos;s go ahead and print out just to make sure that we&apos;re not crazy&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:22&lt;/strong&gt; · ah there we go oh this is a hot rail oh dear and we need to reload from the beginning for missing anything okay let&apos;s try that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:00&lt;/strong&gt; · again it is saying picking color so there&apos;s something else going on okay I&apos;m gonna look up my image picker that I made just real quick&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:01&lt;/strong&gt; · let&apos;s see file access&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:23&lt;/strong&gt; · but this the class let&apos;s see what we need to do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:57&lt;/strong&gt; · hmm interesting file upload input element we did this with the color but this is different let&apos;s see if we&apos;re doing the wrong type hidden&apos; input let&apos;s try that one hope&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:34&lt;/strong&gt; · let&apos;s go to the definition&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:03&lt;/strong&gt; · let&apos;s go back to that go ahead and stop the application to oh because she&apos;s in input element of type oh here we go let&apos;s try that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:58&lt;/strong&gt; · that&apos;s what we want let&apos;s try that it&apos;s going around again&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:23&lt;/strong&gt; · does not support Internet Explorer useful hmm see if we&apos;re doing anything wrong&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:07&lt;/strong&gt; · yeah I think it might be quick instead of so large&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:31&lt;/strong&gt; · okay let&apos;s see what happens awesome hey there we go so that&apos;s perfect so now you need to take the callbacks so let&apos;s look at this again so we got a couple&lt;/p&gt;
&lt;h3&gt;Callbacks&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;23:02&lt;/strong&gt; · different listeners we got input listener oh this is a different one sorry it&apos;s our load let&apos;s give it a fallback color - it looks like people are early looks like that&apos;s a thing so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:48&lt;/strong&gt; · awesome are we gonna fall back to white and let&apos;s do our callback&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:07&lt;/strong&gt; · mm-hm Justin uptick first is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:28&lt;/strong&gt; · mm-hmm so we called for every value and then this one is called for glass value interesting so we probably would like to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:46&lt;/strong&gt; · do this in a strange kind of based but we still want to have the option to pick the first one let&apos;s build it with this first so let&apos;s go ahead and create this into a future st. for the implementation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:12&lt;/strong&gt; · and same for the web person for the yeah you know I mean okay let&apos;s do take first&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:34&lt;/strong&gt; · so when you add an event listener input see if there&apos;s a different way let&apos;s call back nope okay plus to our value&lt;/p&gt;
&lt;h3&gt;Event Listener&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;26:09&lt;/strong&gt; · which is Hopi a string&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:28&lt;/strong&gt; · selected color is not equal to null we&apos;re going to kind of wait for this we&apos;re gonna also break it out when they cancel so you can set that here see it does do select right here which is weird but it&apos;s fine&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:00&lt;/strong&gt; · use capture also let&apos;s go ahead and do that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:42&lt;/strong&gt; · it&apos;s definitely the target value how we may need to cast it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:10&lt;/strong&gt; · okay perfect let&apos;s do this one right here and now we want to return the color and we have a function for that too because now we want to convert it back to a flutter color so color from string&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:38&lt;/strong&gt; · there we go let&apos;s use our same implementation turn color from string awesome now this may go into an infinite loop on the first time but let&apos;s go ahead and just make sure we print out these two different listeners&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:24&lt;/strong&gt; · okay let&apos;s go insert application&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:46&lt;/strong&gt; · and you do air handling next to so we have our debug window select it interesting okay it closed still receiving them see if we can print this out&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:37&lt;/strong&gt; · okay let&apos;s try that again so we immediately returned a big thing how&apos;s it equal to null that&apos;s what we need&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:07&lt;/strong&gt; · oh look at that there is our color that is perfect now what we could do well say we need to do in a functional way I just have a call back it might be better in this approach let&apos;s do this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:49&lt;/strong&gt; · we&apos;re going to do function color [Music] and value there we go let&apos;s go ahead and add that to our unsupported and let&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:14&lt;/strong&gt; · add to the web because in actuality I mean they can pick multiple colors not just at one time and the user could just stop listening once they get the first one but I mean in theory they could just have this thing up to their side doing whatever just go ahead and choose huh now we can remove our while loop and put this here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:56&lt;/strong&gt; · okay perfect perfect and now we need up this ooh yeah change that does not look good too [Music]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:33&lt;/strong&gt; · see I was fine I can actually make this there we go since it could be optional so you get optional here let&apos;s go back to our example gonna have a color so this is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:08&lt;/strong&gt; · gonna be our value each time that we want to kind of pass it in and you do if on to set state color is equal to color and we can also print it out just so we can see what it is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:33&lt;/strong&gt; · and for to be fun let&apos;s go ahead and set this as the background color run the example&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:02&lt;/strong&gt; · Paul goes well we should be able to change the background-color each time there you go oh look at that that is sweet that is really cool and what&apos;s crazy is we can actually do an eyedropper to itself that is wicked cool&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:39&lt;/strong&gt; · interesting it stops listening for that let&apos;s try to you oh that&apos;s awesome cool wow that is so cool alright let&apos;s go ahead and quit that now let&apos;s go ahead and handle edge cases&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:08&lt;/strong&gt; · okay this looks really good so you gotta remove that old code probably want to this could be null so we can actually wrap this as a callback so date color&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:55&lt;/strong&gt; · okay and then we can do try-catch right here for the edge case that it&apos;s over null and then right here we can try to see if it&apos;s not null okay let&apos;s go ahead and print this here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:27&lt;/strong&gt; · perfect cool we have a way to do this on desktop too but I&apos;m just trying to debate if we want to or not they can do desktop and mobile&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:54&lt;/strong&gt; · at the moment but web would be the one that we get Smith the one point out with yeah that&apos;ll be good so let&apos;s after you query package you need to update the change log so here to added color for a web change the version here so let&apos;s update the example so let&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:33&lt;/strong&gt; · create an icon button here whoops what I do there done this one Oh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:57&lt;/strong&gt; · too many yeah that&apos;s weird no I love let&apos;s try that again oh here we go perfect do the pallet okay select that I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:21&lt;/strong&gt; · like to add it here usually you can do the three back ticks with dart to have the syntax highlighting I&apos;m going to copy this add this here and then I like to usually copy the function so we can kind of get a call back directly&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:25&lt;/strong&gt; · since that&apos;s by default we go cool you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:55&lt;/strong&gt; · and I spelled that all right um yeah that looks good now we just need to publish first this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:19&lt;/strong&gt; · is another thing that I like to do this allows you to do demos so let&apos;s go ahead and CD into the example its to flutter build web so we can give a release build and while we&apos;re doing that we need to update let&apos;s go ahead and add it to God&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:53&lt;/strong&gt; · let&apos;s do add existing packages go right at the top perfect create a positive it is perfect&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:22&lt;/strong&gt; · so either both the thing here let&apos;s go ahead and open this up and finder actually I created a git repository inside of the example so you go ahead and delete that awesome I think base project make it public for all you guys let&apos;s go and publish that I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:06&lt;/strong&gt; · build is done so we&apos;re going to go in and copy to build web folder copy all the contents and paste it inside Docs to adding demo I do the same process on all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:30&lt;/strong&gt; · my packages so if anyone&apos;s curious just go ahead and add github pages and the docs folder copy that URL&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:52&lt;/strong&gt; · going at it here it&apos;s always nice to have topics and let&apos;s go back to a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:07&lt;/strong&gt; · project and right here I want to do online demo suite and since I have other packages to I like to add the banner for what I have on most of my stuff so here we can go to plugins&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:39&lt;/strong&gt; · let&apos;s do flutter MIDI yeah now let&apos;s do something else oh I need to add one there student&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:56&lt;/strong&gt; · master crunch well we need to add these anyway so go ahead and add these at the top oh it is there okay so now we can swap this out perfect and less time to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:22&lt;/strong&gt; · publish you gonna do flutter back let&apos;s go ahead and clear this flutter let&apos;s go back up one directory widget flutter package is published we might get an error that&apos;s okay yep there&apos;s a there&apos;s a weird issue on the master branch right now flutter but that&apos;s okay we can do public trees publish that&apos;ll force it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:00&lt;/strong&gt; · interesting what are we missing your author&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:31&lt;/strong&gt; · perfect that&apos;s what we want click yes and we are good to go so now if we update this and then view this one your hope&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:02&lt;/strong&gt; · and this should work make sure that&apos;s correct give color picker you&apos;ve got a bigger little calibrator I just probably cuz that&apos;s new&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:30&lt;/strong&gt; · very fresh weekend all right let&apos;s try the demo perfect we need to fix that so also show you how to send update&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:05&lt;/strong&gt; · you sure we called it correctly yeah I come increment looks good to me let&apos;s try it again let&apos;s just make sure our example works [Music]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:51&lt;/strong&gt; · okay so it is still working we need to also disable that for here flutter or debug check one painter falls oh of course we need to also add a dark theme for the demo all right let&apos;s go ahead and build it again and we&apos;ll just copy those files up&lt;/p&gt;
&lt;h3&gt;Dark Theme&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;49:55&lt;/strong&gt; · let&apos;s go ahead and copy those it&apos;s good I thought just go ahead and see if it&apos;s building occupied just first before we publish&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:33&lt;/strong&gt; · interesting is only working on Chrome&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:50&lt;/strong&gt; · okay so probably have an edge case for that but that&apos;ll be another video all right thanks for watching if you want to see more like this be sure to subscribe just thought it would be cool for anybody that&apos;s curious about package and plug-in development for flutter reach out to me I can follow me on github and Twitter and everywhere else yeah thanks again&lt;/p&gt;
</content:encoded></item><item><title>Introducing the signals_hooks package for Flutter hooks and signals</title><link>https://rodydavis.com/videos/flutter/introducing-the-signals_hooks-package-for-flutter-hooks-and-signals/</link><guid isPermaLink="true">https://rodydavis.com/videos/flutter/introducing-the-signals_hooks-package-for-flutter-hooks-and-signals/</guid><description>The `signals_hooks` package introduces a simplified way to manage state in Flutter applications by integrating signals with the Flutter Hooks package, offering a powerful alternative to traditional `setState` methods.</description><pubDate>Sat, 01 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Introducing the signals_hooks package for Flutter hooks and signals&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;732LqgfYbDQ&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Website: https://rodydavis.com
Twitter: @rodydavis
BlueSky: @rodydavis.com
Github: @rodydavis
Patreon: https://www.patreon.com/rodydavis
Buy Me A Coffee: https://www.buymeacoffee.com/3mgFNYd&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · hi my name is Roy so today I released a new package called signals Hooks and you may be wondering how that might work or how you can use it in your flutter apps so today I&apos;m going to create a quick example on how you normally would work with variables and flutter how we migrate it to signals and how signals hooks can make it even easier when working with the flutter hooks package all right let&apos;s get started so first I&apos;m going to go to my flutter idx instance&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:30&lt;/strong&gt; · and I just have a default counter example this example does nothing more than you set State and this is probably something you&apos;re very familiar with in fact we can go in here and create this a value Notifier and this should already start to add some ergonomic benefits we can remove the if state&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:57&lt;/strong&gt; · check here and we want to to wrap this one with a value listenable Builder so usually I wrap it with Builder value listenable Builder we&apos;re going to do count and we can get out the value&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17&lt;/strong&gt; · and the child awesome so now when we restart we get a count update as normal so this is already really really cool and offers a unique advantage over set state but let&apos;s take it further with signals so let&apos;s go ahead to your terminal and do flutter Pub add signals this will update your Pub spec yaml and allow us to import it inside of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:48&lt;/strong&gt; · um here so we can change this to Signal importing the signal flutter package and everything else stays the same and because signals created after 6.0 are value listables and value notifiers we don&apos;t have to change any code which is really cool if we wanted to be uh use more functions out of the modern signals package we can do the new signals mix in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:18&lt;/strong&gt; · which allows us to change this up a bit and do create signal and we can remove this unneeded Builder since this will automatically get rebuilt every time that we change our&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:39&lt;/strong&gt; · value so here as we expect count increments so I think this is already really cool offers a lot of advantages over just defaults at State uh the more we chain computed the better our performance will get but one thing that has been a challenge when working with signals and flutter is you still want to be able to create reusable logic&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · with signals and I think this is actually where um hooks is actually going to help so we can go to our terminal and do flutter Pub add flutter Hooks and the new package called signals&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:22&lt;/strong&gt; · hooks so for those that are probably not used to hooks in flutter this hook books is a kind of replacement for State full widget which allows you to bind to the life cycles of disposing en knitting and having logic that&apos;s collocated with that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:43&lt;/strong&gt; · single widget but to be able to be shared across multiple stateful widgets and it&apos;s actually really easy to migrate so we can just go in here and call hook widget as our new extension we can go and delete all this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:05&lt;/strong&gt; · perfect and we going to remove this remove this because now this is going to go inside of the build method U I&apos;m going to use the default um hooks implementation so we&apos;re going to do use&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:26&lt;/strong&gt; · State and for the on press call back we&apos;re going to do value Plus+ under the hood this is actually a value Notifier which is very similar to what we just talked about and because signals automatically does a two string method that&apos;s reactive we have to explicitly declare it here awesome really cool so now how do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:54&lt;/strong&gt; · we use the new signals hooks package well we can go in here and instead of you State we can do use signal and we can import it and now remove that unnecessary&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:11&lt;/strong&gt; · call and one final time it rebuilds as expected what&apos;s really cool about this is we Define this logic inside of our build method and it doesn&apos;t matter how many times we call the build method this signal only be called once based on how normal hook are created and we can go in here and do other cool things like count Str Str which is a use computed that we will explicitly call&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:42&lt;/strong&gt; · count value to string and when we grab this we can add it into&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:57&lt;/strong&gt; · here I&apos;m going to rebuild this this is now pulling from the computed value which is then pulling from the The Source signal and we can also even add effects right inside of the build method so we&apos;re going to do use signal effect which is different than the use effect in hooks which allows you to react only when specific signals change so we can go in here and do print&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:27&lt;/strong&gt; · count value [Applause] and hit save and now in our terminal right here we can see through our log messages if I pull it up there we go it&apos;s only being called&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:51&lt;/strong&gt; · one time you don&apos;t get the exponentially increasing number of listeners every time that it calls so this makes it really awesome to be able to take this logic put it inside of this build method and then be able to share it with other components not only that you can then share these signals um across multiple places and even create sub classes uh one final thing I will show you is this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:16&lt;/strong&gt; · new function that I added for defining Global signals and to be able to bind to them they don&apos;t have to be Global but um I just wanted to show how this would look like so we can say like save state would be signal&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:33&lt;/strong&gt; · zero and we can swap out instead of use signal we can do use existing signal and instead of a value we can pass in our existing signal this will mean that we can have multiple widgets all bound to the same Source signal and it still works as expected because this will actually update its parent signal so yeah I think that&apos;s it for this video uh please let me know if you have any questions and I will try to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:02&lt;/strong&gt; · respond to them reach out to me on Blue Sky and Twitter as well as GitHub thanks for watching bye&lt;/p&gt;
</content:encoded></item><item><title>IDX &amp; Flutter by Rody Davis, Senior Developer Advocate - Google</title><link>https://rodydavis.com/videos/flutter/idx-flutter-by-rody-davis-senior-developer-advocate-google/</link><guid isPermaLink="true">https://rodydavis.com/videos/flutter/idx-flutter-by-rody-davis-senior-developer-advocate-google/</guid><description>Rody Davis discusses how IDX and Flutter integrate for enhanced development, covering topics from local vs. remote environments to practical use cases.</description><pubDate>Tue, 29 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;IDX &amp;amp; Flutter by Rody Davis, Senior Developer Advocate - Google&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;jhNzq-zF98E&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;@RodyDavis  spoke about IDX and Flutter and demonstrated how much easier life becomes for a developer when using IDX&lt;/p&gt;
&lt;p&gt;#flutter #idx #dart #developer #google #fluttersiliconvalley&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:05&lt;/strong&gt; · okay awesome uh so cool to see everybody thanks for um be able to uh show up to this awesome Silicon Valley Meetup uh my name is roie and I am super excited to talk about how idx and flutter uh work good together I recently joined idx and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:21&lt;/strong&gt; · I&apos;m a senior developer relations engineer on the deil team but I&apos;ve been at Google for a while been using uh flutter uh for quite a bit and also contributing as well um if you don&apos;t take anything else away from today&apos;s session I want you to go to this URL and try out flutter in idx uh We&apos;ve&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:40&lt;/strong&gt; · uh implemented a lot of stuff to make it a lot better have some exciting new features that we&apos;ve been uh prototyping and uh just make sure you take a screenshot and uh and and a photo of that also if you have any questions that I don&apos;t cover or if you want to talk to me after um or even want to reach out on GitHub LinkedIn or Twitter you can just find me ready Davis&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01&lt;/strong&gt; · so today we have a lot to cover but I think it&apos;s going to be worth it uh first off we&apos;re going to start with uh local versus remote development environments then we&apos;re going to move on to Nyx and Docker finally talk about flutter and idx and then hopefully end on some cool use cases with Native tooling uh but first let&apos;s start off with a question how many people have used idx raise your hand awesome how many people have used idx in the last month awesome sweet yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29&lt;/strong&gt; · if you haven&apos;t checked it out recently I highly suggest you to check it out again we have some uh like I said a lot of really cool stuff coming so the first question you might be thinking is like why would I want to use idx or NYX if I already have flutter set up locally so this is a valid uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47&lt;/strong&gt; · question that we we can explore one feature requests we get a lot is with idx is we want to be able to support offline in a local version but first we really need to take a step back and think about what the actual problem we&apos;re trying to solve here is you know we what we&apos;re exchanging for a local setup and when it makes sense to do so I want you to think back to the first time that you&apos;re installing any SDK at all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:13&lt;/strong&gt; · usually there&apos;s a lot of uh trouble getting started not necessarily because of the install guide but because of you are a day one developer exploring this new framework you don&apos;t know what the happy path is other than the documentation that you usually find and uh so it&apos;s important to remember&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:33&lt;/strong&gt; · that that happens for every user every new time they visit a framework it also happens when you revisit a project after two years of not opening it you are crushed to it again the point is that there&apos;s a lot of unknown and error States you can end up in and you can easily fall into and everything&apos;s fine&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:51&lt;/strong&gt; · and still it&apos;s not but if you&apos;ve been working with flutter for longer than uh for a longer period of time then you&apos;ve sure run into the problem where you&apos;ve had multiple flut version conflicts uh if you have production apps this problem is worse because sometimes you may need to pin onto a specific version this is not unique to flutter this exists for every framework out there there&apos;s many people stuck on angularjs or angular one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:16&lt;/strong&gt; · because they&apos;ve launched into production and there&apos;s some maybe reasons why dependencies are holding you back and this problem is even compounded when you bring in external dependencies like whether it be cocoa pods uh xcode Max versions local clis any additional native tooling rust C++ and go all this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:33&lt;/strong&gt; · compounds together to have a specific version that you&apos;re tied to and a lot of these things have workarounds but they&apos;re usually specific to the development environment and the Machine and the intersection of these dependencies so if you work on multiple text Stacks Beyond just flutter you&apos;ll see that this problem gets expanded for example if you have uh we&apos;re working on python there&apos;s a really big migration to Python 3 and a lot of people ran into&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:00&lt;/strong&gt; · cases where you needed to either install both versions and then now have to deal with these conflicts I remember when I first got my M1 Mac um MacBook there&apos;s a lot of issues where you had to compile both the arm and the x86 versions and a lot of times they were conflicting you had now had up aliases duplicates of most binaries so you can see how this uh gets expanded out and if you&apos;ve used PHP you know there&apos;s virtual environments and so on and this problem gets worse over time&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:27&lt;/strong&gt; · when you get a new machine or you&apos;re trying to set up or even come back back to it and right now when you deploy your code to GitHub or other gith hosta providers it&apos;s really specific to what you need to use to install the app but not necessarily opinionated on where you need to run it GitHub actions can specify what&apos;s needed to build parts of it but not necessarily what you need for developing it so this is all just for your current&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:54&lt;/strong&gt; · projects but what about what happens when you want to explore something new if you&apos;re start developer and you want to try out rust or a PHP framework or even running a native tool chain like C or C++ then exploring new projects becomes really difficult because you need to install it before you try it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:12&lt;/strong&gt; · there&apos;s also some tools and web Frameworks out there that let you run them in a browser but these are mostly for JavaScript only kind of applications the goal is to try out the framework to see if you like it before taking the leap and installing it locally but what about Docker you might be thinking I think uh everybody&apos;s seen the where where U you know if it works on my machine well then let&apos;s ship your machine that&apos;s literally how Docker was born but Docker only solves the problem&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:40&lt;/strong&gt; · of the environment for building it it and also sometimes running it self-contained this this deployment can be gigabytes on this container image because you&apos;re literally com putting it all together and if you have multiple versions you need to have multiple containers of them it also doesn&apos;t make a lot of sense to use a Docker file for your local development setup although there are some cases out there with Dev containers and some other stuff that try to make a um a leap into this but the problem is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:07&lt;/strong&gt; · it&apos;s also gigabytes it can be very slow but so this is just taking a look at like um a Docker for development Docker files are a way to describe everything you need to build and run an application but really it&apos;s a great way to deploy an application the problem that we have with this file is that everything is imperative and not de ative it requires you to specify all the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:32&lt;/strong&gt; · commands that will be run on the VM but there&apos;s no promise about version conflicts that they there&apos;s no promises about directory clashes or even cashing of true dependencies because if you run two commands that both intersect in the same directory they&apos;re going to overlap and you can have conflicts that&apos;s not docker&apos;s fault that&apos;s just a you know one of the natures of how you&apos;re running you&apos;re literally just getting a VM and running commands but I think we&apos;re on to something here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:01&lt;/strong&gt; · what we want to do is declaratively set up the operating system and everything we need to get started so introducing NYX raise your hand if you&apos;ve heard of Nyx before hey there&apos;s some people uh NYX is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:16&lt;/strong&gt; · a functional package man manager that lets you deterministically build dependencies that do not conflict NYX is declarative where Docker is imperative in fact NYX can build layered Docker images better than even Docker can and this is because NYX can specify all the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:34&lt;/strong&gt; · caching and the dependencies separated so that you can deploy without ever having to worry about them conflicting but before we look at NYX and idx I want to briefly go over what idx looks like and what it can do so first of all it&apos;s entirely web based you just need to visit a URL and you can start coding right away it&apos;s also built on vs code so it&apos;s incredibly familiar to most steps you can bring most of what you use uh in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:03&lt;/strong&gt; · your local environments your preferences and your extensions in your muscle memory right to the browser it&apos;s important to note that this is a full Cloud VM this is not limited to things that only run in the browser this is a real Linux machine behind the scenes that runs NYX and you have access to this in the full NYX workspace one of the major challenges When developing on the web for mobile apps is actually testing out your application so with idx we have a built-in Android&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:36&lt;/strong&gt; · emulator and we&apos;ve also got several Google Integrations which make it easier to add apis like gemini or even deploy to Cloud run or Firebase hosting and just about every Google surface in 2024 Gemini is built on and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:51&lt;/strong&gt; · you can get help in understanding generating and improving your code right inside idx there&apos;s also Integrations that help it make you trouble shoot errors with AI and so much more in fact we just launched a feature that&apos;s in preview where you can use Gemini to read and write files and run terminal commands some very exciting stuff and finally the team is actively working on realtime multiplayer collaboration getting this right has been tricky but the team is working really hard on it so now let&apos;s take a look at nxs in idx we use a file called dev.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:24&lt;/strong&gt; · NX this is what you share across team and it saves inside of your get repo David East puts it in a great way def. n is like a package Json for your entire development environment or in this case pups Cel and a way another way to think about it is where terraform is infrastructure as code this is for your production environment dev.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:49&lt;/strong&gt; · NX is a Dev environment as codee for a development environment de. next Define just about everything you need to work and run your app in the cloud from system packages and services to compiler tool trains and local database and providers to Ida extensions and also language support and debugging to the specific wiring needed to boot up your front end and backend service automatically so you can preview your app this is all defined in one file and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:23&lt;/strong&gt; · one thing to important to note here is dev. Nix is built on the open source Nick ecosystem so there&apos;s already an exist in deep Community to pull from that we can run the world software in this kind of environment so let&apos;s take a look at this example this is from our official data connect template it starts by specifying the Nyx Channel which is 23. uh 11 in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:45&lt;/strong&gt; · this case it uses a specific version here so we can have reproducibility this is your so your Dev environment work the same two years later one thing about NYX is we have NYX channels and so anytime you want to upgrade and you maybe want to check and see if like you&apos;re using Deno and you want to make sure it&apos;s using the right version you can go update your channel and all your dependencies will work better&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:09&lt;/strong&gt; · together next this is where all the system packages you want are installed and are available here we&apos;re specifying that we want no js20 installed but you could also add Java python Ruby go and Russ and any other compiler tool chain out there usually just with one line next we have a lot of common services that we want to make sure that we install in Buddha when our workspace starts so we can do this with just one line of code or you could also ensure that you have maybe PG Vector installed&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:37&lt;/strong&gt; · and enabled and even pre-seeding your database we could even load Docker and Rus and have this all seated right into an our application next we have IDE extensions now because ID idx is built on top of vs code we support most of the extensions that people are going to want one of the caveats though is that idx and any other for CVS code relies on open vsx for the published extensions these usually not a problem because most extensions are duly&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:07&lt;/strong&gt; · published now we have two life cycle Hooks oncreate and onart oncreate is when the user first creates their workspace template and it&apos;s the first time that they&apos;re booting it inside of idx it lets you do a one-time setup like running or installing or CD your database and the second is a life life cycle hook called on start this is not here but it&apos;s particularly useful for every time that your application starts and you boot up your workspace again in this case uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:37&lt;/strong&gt; · since we&apos;re not we&apos;re using the data connect template we only use the install scripts in the mpm1 and so we just Boot It Up Inside the preview section and speaking of which so we have a preview section and this is where you can Define the Android and uh web&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:54&lt;/strong&gt; · previews the web preview boots up your app on an HTTP server and shows it inside of an embedded browser inside the IDE and there&apos;s also an Android preview which runs inside of an Android emulator and it is a full Android emulator that you can do anything you would do locally you&apos;re what you&apos;re seeing here is a data connects template called npm runev on the command line it&apos;ll ensure that you run the preview server on a specific Port but that&apos;s about the only restriction now there&apos;s so much more to dev.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:26&lt;/strong&gt; · NX and if you want to learn more I highly suggest you check out the idx blog uh and David just did a really awesome video recently about it as well so next let&apos;s learn about idx and flutter you can get started today with flutter and idx using our default flutter template or you can clone any flutter repo as long as you add the correct dev. n file but first I want to quickly demo a fun use case that should apply to a lot of you you can enable flutter or Dart uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:58&lt;/strong&gt; · for Developers right away on any Pub package right in the browser without installing anything so I am going to share this real quick&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:32&lt;/strong&gt; · okay awesome so what I have here is just a blank uh idx workspace with the um the default flutter uh dev. next already installed I removed the default flutter app just uh to show it here so what I&apos;m going to do is do uh flutter create SLT template plugin&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:57&lt;/strong&gt; · ffi platforms Android and we&apos;ll install it locally uh actually we&apos;ll do examp okay that that&apos;s creating um what we&apos;re going to do is uh we&apos;re creating an ffi template if I don&apos;t know if you&apos;ve ever used this before but what&apos;s cool is it uses uh C to be able to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:26&lt;/strong&gt; · create your application uh plugin and call into it but what&apos;s important about that is flutter needs to use and compile the C uh the C ffi plugin to load into inside your app so here we have the example and we need c the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:45&lt;/strong&gt; · important thing about this is you know we need c on the machine to install but here&apos;s a typical setup that most people are usually familiar with we have a flutter app or a flutter plugin with a example subdirectory so with idx it&apos;s actually really easy to support this use case um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:02&lt;/strong&gt; · what we do is we have this dev. next which is the default one you get with the template and we come down to the previews um one of the cool things about the preview section is you can comment out things that don&apos;t make sense so for the ffi plugin since it&apos;s only running on uh only supports native platforms we can common out um the web one and then&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:22&lt;/strong&gt; · the other thing we want to do is we actually want to run the subdirectory as opposed to parents so we can do is change working directory here we can do example and once we hit save we have this option to rebuild our environment this is what uh allows idx and Nyx to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:42&lt;/strong&gt; · rebuild our OS declaratively and to be able to roll back if there&apos;s any changes uh what&apos;s really cool is it&apos;s kind of almost like get uh if you ever use NYX you can literally roll back to snapshot so you&apos;re always guaranteed to be in a working state or get back to one so here because we have uh the Android One open we noticed that it didn&apos;t launch the web which is fine it is exactly what we want and um now it&apos;s just compiling and running our application so it should just take a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:14&lt;/strong&gt; · second and let me open up the examples well raise a hand if you&apos;ve used the ffi plug-in template yet and flutter awesome should just be a second I also have a video demo just in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:55&lt;/strong&gt; · case if it literally doesn&apos;t start in the next 30 seconds oh the drys of live&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:27&lt;/strong&gt; · demos oh all right let me go forward but anyways what it does is it launches the uh flutter application on the example subdirectory and I&apos;m just going to reload it just one more time just to make&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:53&lt;/strong&gt; · sure there we go awesome so this is uh when the app boots up we actually installing and running the application but this required compiling the package which then compiled the native C tool train so uh super exciting but it&apos;s kind of showing that you can use native dependencies already out of the box but also be able to support uh your flutter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:22&lt;/strong&gt; · package let me show the tab again okay cool um so with idx we can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:39&lt;/strong&gt; · also add an open and idx button directly on your read me which will open up your idx workspace the important thing here is that you as a library author can set up the de development environment because you know what Native tooling is needed to get the set up we can also use this online configurator which makes it super easy to build an add the button into your docs to find this you can go to the idx documentation on developers.google.com idxg openin idx uh this will also work&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:09&lt;/strong&gt; · in both light mode and dark mode so uh uh I I use it all the time to to make these quickly next I want to talk about Native tools and flutter when you&apos;re building production applications sometimes you need native tool chains that exist beyond what flutter and dart provide this could be rust plugins or even build ffi plugins with C so I have one last demo and I think that this will also be a cool one to show uh let me pull this over&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:43&lt;/strong&gt; · here who you has used a SQL light before or even Vector databases awesome Okay cool so uh this next demo is uh if anybody&apos;s heard of sqlite V&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:00&lt;/strong&gt; · um yes cool uh sqlite VC is a a really cool way how you can uh do Vector embeddings directly in sqlite and what I have here is not only uh sqlite working in flutter but it&apos;s sqlite working in flutter through an ffi plugin built in C&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:18&lt;/strong&gt; · which is then compiled from a rust host so you have a rust project compiling a c project compiling all the way down to ffi plugin but what&apos;s cool is this is fully extensible so here we have a really uh large amount of dependencies that we need to bring in but NYX makes it super uh Compact and easy to include these we even can specify custom subdirectories We Can Vendor and have all of our hooks so this is just I wanted to show kind of like a more complete example of uh idx and Native&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:49&lt;/strong&gt; · tool chains but what&apos;s awesome is like this is something I actually needed to use because I ran into trying to get rust installed also working with SQL light on my local machine while working with Homebrew and I&apos;m like on a Mac but I needed an environment that compiled this with Linux because the library author is using Linux to build the application so what was cool is I was able to basically specify all the L uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:13&lt;/strong&gt; · Linux dependencies and then now I can be building and testing it running it right in the browser so uh all that to say is uh there are so many really cool things that you can do uh when you have one file to configure&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:35&lt;/strong&gt; · everything stop this and show here so that&apos;s um pretty much everything I wanted to uh cover today if you um didn&apos;t see it already make sure you go to ID ex. google.com new flutter and I I&apos;m also happy to answer a couple questions about idx but uh thanks so much for having me I can&apos;t wait to uh talk more about&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:07&lt;/strong&gt; · it all right any questions yeah because last time with short with chrome short and that you mean opening up idx but sshing into another workspace yeah just like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:33&lt;/strong&gt; · yeah I would love to explore that option I don&apos;t think the de team is explore sshing from another machine with that but yeah I think that&apos;s it should be possible now with a lot of the things we got it with the host layer so yeah comp with shortcuts yeah that they&apos;ve also been improving the shortcuts a lot yeah any other questions yeah how well paid or not well paid we develop really want my locals code I also find super&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:05&lt;/strong&gt; · cool yeah so uh one of the cool things about the dev. NX is you just check it into GitHub so there&apos;s a world in which you have your project checked out both locally and in in idx you can commit and pull changes all right like with GitHub so uh it&apos;s actually what I use for a lot of my flow is I&apos;ll I&apos;ll like to test my app like in a Linux environment make sure it&apos;s building and then that also helps with GitHub access and other stuff too and then I have my local environment for a snapshot or branches that I&apos;m working on yeah other questions follow&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:37&lt;/strong&gt; · to that so do you have a giup plugin sorry is it a giup sorry G plugin in the idx directly yeah you can just yeah we we support GitHub bitbucket and um gitlab yeah you can also do any public get repo but those are the ones that support authentication and committing back yeah it is just an kind of opinion on how what do you think this with workflow for cross platform and being B in use&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:09&lt;/strong&gt; · IX same thing with the B code and you have to go back to the X code but if you want to build on an iOS you have to use xcode anyway what do you think is the best best workflow would be to use idx and again use&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:24&lt;/strong&gt; · x yeah I think if you&apos;re um if you&apos;re building any kind of a solution that need needs to use like you know Mac OS and iOS um then you&apos;re probably going to want be wanting to use a native tool chain right now um otherwise you can always do things like xcode Cloud build if you want to deploy it uh remotely and there&apos;s like code magic too any other questions cool uh well definitely come find me after if you have any other questions that weren&apos;t covered but uh yeah thank you so much&lt;/p&gt;
</content:encoded></item><item><title>Material 3 from design to deployment</title><link>https://rodydavis.com/videos/flutter/material-3-from-design-to-deployment/</link><guid isPermaLink="true">https://rodydavis.com/videos/flutter/material-3-from-design-to-deployment/</guid><description>Learn how to transition a design from Figma into adaptive Flutter components using Material 3, ensuring a flexible user experience that accounts for multiple device types and input methods.</description><pubDate>Wed, 25 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Material 3 from design to deployment&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;7nrhTdS7dHg&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Learn how to build the basil material study in Flutter with advanced theming techniques that support multiple device contexts.&lt;/p&gt;
&lt;p&gt;Speakers: Rody Davis, Liam Spradlin&lt;/p&gt;
&lt;p&gt;Watch more:
Watch all the sessions → https://goo.gle/FlutterForwardYT&lt;/p&gt;
&lt;p&gt;Subscribe to Flutter → http://goo.gle/FlutterYT&lt;/p&gt;
&lt;p&gt;#FlutterForward&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] LIAM SPRADLIN: Hi, my name is Liam.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:09&lt;/strong&gt; · I&apos;m a senior design advocate at Google on Material Design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · RODY DAVIS: And my name is Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:13&lt;/strong&gt; · A senior developer advocate at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:15&lt;/strong&gt; · LIAM SPRADLIN: Today, we&apos;ll be talking about translating a design into code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · We&apos;ll be breaking down how to take a design from a Figma file to creating adaptive reusable components for Flutter that target all six supported platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:29&lt;/strong&gt; · An important thing to remember is that adaptive design is not just about the breakpoint and device target.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · When building a truly adaptive experience, there&apos;s a whole matrix of options to consider.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · For example, a tablet with a keyboard but no mouse, a desktop with only a touch screen, or a mobile version that&apos;s running on a desktop in free form.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · With Flutter, it&apos;s easy to create platform adaptations and build a flexible UI that&apos;s capable of targeting the correct platform and input method.&lt;/p&gt;
&lt;h3&gt;Pesto - Case Study&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · To illustrate what this means, particularly as you build adaptive apps with Flutter, we&apos;ve made a sample study.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:59&lt;/strong&gt; · A recipe discovery app called Pesto.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · It&apos;s an app that has several different user journeys that can take place across different device types, input modalities, and contexts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · For example, you might enter and manage your recipes on a computer screen, browse for inspiration on your tablet, and carry out the recipe on an ambient device like a Nest Hub.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18&lt;/strong&gt; · From a designer&apos;s perspective, we build layouts and experiences for apps like Pesto with a ton of contextual information in mind, meaning that our intention for the experience, how it works, how it feels, the character of the design is embedded deeply into the artifacts of our work, often without explicit markers for the people we&apos;re working with to bring it to life.&lt;/p&gt;
&lt;h3&gt;Design Rationale&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:38&lt;/strong&gt; · Things like the grid structures that underlie our layouts, the navigation interactions we intend, theming and visual customization, and the motion and transitions that link everything together are just a few of the key aspects that we need to accurately and reliably convey to developers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · In this talk, we&apos;ll walk through some of these key elements in reference to our example app and talk about how Flutter can preserve and convey your design intent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · RODY DAVIS: From a developer&apos;s perspective, we tend to think about designs as a set of components that we can post together in higher order UIs. Flutter gives us the ability to build declaratively on every frame and any size.&lt;/p&gt;
&lt;h3&gt;Developer Rationale&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:14&lt;/strong&gt; · When targeting six platforms, there&apos;s a lot of things we need to consider such as input methods, touch targets, keyboard events, native menus, file handlers and more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:23&lt;/strong&gt; · We also need a translated design in Figma with dummy data into a working demo using mock data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:29&lt;/strong&gt; · This may be a tempting step to skip when building with Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:32&lt;/strong&gt; · But by starting with dummy data, you can decouple your UI from the business logic and start working off of a few model style presentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:39&lt;/strong&gt; · And finally, we need to think about all the animations not shown in Figma or even implicit ones for transitions throughout the app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · Having a way to quickly test various animations is critical.&lt;/p&gt;
&lt;h3&gt;Material Design in Flutter&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:50&lt;/strong&gt; · One exciting update is the new Material 3 widgets for Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:53&lt;/strong&gt; · There are multiple widgets that are included.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:55&lt;/strong&gt; · And we&apos;ll be going over how to use them today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:58&lt;/strong&gt; · As we go through this talk, we&apos;re going to be building the Basil Theme together.&lt;/p&gt;
&lt;h3&gt;Building the Basil Theme&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · This will cover theme extensions, custom fonts, dynamic color, and widget themes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:07&lt;/strong&gt; · First, let&apos;s create the Material 3 Theme class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:12&lt;/strong&gt; · Here, we&apos;re creating a immutable class with some brand colors that the design defines for us in a simple constructor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · Brand colors, if you&apos;re not familiar with, are colors associated with the company and colors that make up the logo or related assets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:25&lt;/strong&gt; · For Google, this is the four colors that make up the G logo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:29&lt;/strong&gt; · Next, we create a callback for creating the theme data object.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:32&lt;/strong&gt; · And we&apos;ll use that to pass to the theme for the Material app later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:36&lt;/strong&gt; · Available in the current version of Flutter, we can enable the use Material 3 theme override, which we will use for the new color system and update any supported widgets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:46&lt;/strong&gt; · After we have defined the class, we can then import it at the entry point of our application and instantiate the class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:52&lt;/strong&gt; · We then just call the two-theme data method and build the Basil theme, which we then provide to the widget tree.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:58&lt;/strong&gt; · Later, we&apos;re going to be talking about ways in which we can theme the application with just the default widgets and theme overrides which allow for better reuse and extensibility.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:07&lt;/strong&gt; · Next, we can take advantage of the theme data feature called Theme Extensions released in Flutter 3.0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:13&lt;/strong&gt; · To create a theme extension, simply extend the class we just created with the theme extension class and the type of our theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:20&lt;/strong&gt; · This is required for when we pass it to the theme data class later and for providing it to the widget tree.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:26&lt;/strong&gt; · We only need to override two methods.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:28&lt;/strong&gt; · The first being the copyWith method.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:30&lt;/strong&gt; · This is for when you&apos;re inside a widget and you want to override a single property and get a new theme back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:37&lt;/strong&gt; · Next, we can just override the alert method, which is used for animation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:40&lt;/strong&gt; · Just check to see if the type coming in is the class we&apos;re expecting and work through all the inputs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:45&lt;/strong&gt; · Now, back to Liam on typography.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:47&lt;/strong&gt; · LIAM SPRADLIN: Speaking as an interface designer who&apos;s also a type designer, it&apos;s important to me to note that type or text in all its expression is interface.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:57&lt;/strong&gt; · It serves so many purposes in an interface from delivering content to aiding in navigation, all the way to elements that are nontextual but still typographic, like icons.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:06&lt;/strong&gt; · One big consideration is how you choose the type styles that best serve your content and layout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:12&lt;/strong&gt; · In space-constrained places, like navigation components, the right type style can allow you to communicate more clearly with users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:19&lt;/strong&gt; · Other type styles, like those found in materials type scale, can help you create and maintain consistent information hierarchies throughout your app and across devices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:28&lt;/strong&gt; · Titles, captions, body text, even inline links can instantly be recognized by users if your type scale carefully considers their styles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:36&lt;/strong&gt; · Making more nuanced choices about baseline grids, columns, spacing, and alignment can also contribute to the overall character of your experience, making it strict, geometric, and modern, or allowing it to be loose, free flowing, or even playful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:50&lt;/strong&gt; · RODY DAVIS: From a developer&apos;s perspective when translating a design, it is important to preserve the semantics around typography, emphasize, spacing, and type scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:59&lt;/strong&gt; · It can be easy just to stick with the default textile in Flutter and just change the font size or alignment as needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:05&lt;/strong&gt; · But there&apos;s a lot we can do to really polish UI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:08&lt;/strong&gt; · For most cases, you can get away with using the text widget in Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:11&lt;/strong&gt; · But sometimes, a design will have a bolded word in the middle of a sentence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:15&lt;/strong&gt; · And for that, we can use the rich text widget.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:17&lt;/strong&gt; · Also, in text-heavy apps, there&apos;s a dynamic list and styles such as in a markdown source.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:22&lt;/strong&gt; · And for that, we can use the Flutter markdown package which will convert our inputs into widgets and rich text.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:28&lt;/strong&gt; · Both of these are reasons why we want to have great custom text themes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:33&lt;/strong&gt; · Often, a design will have multiple font pairings that corresponds to titles and headlines or body and text.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:39&lt;/strong&gt; · Flutter gives us the text theme class, which we can then use to define our type scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:43&lt;/strong&gt; · And we can include any number of font families for the levels.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:48&lt;/strong&gt; · To actually load the font, we can either include the font assets directly in our app and define them in the pubs of a Caml or use the Google Fonts Pub package and dynamically load them in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:58&lt;/strong&gt; · Using the Google Fonts package allows us to test different font pairings before optimizing for production build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:04&lt;/strong&gt; · First, we need to import the Google Fonts package in the file where we defined our theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:09&lt;/strong&gt; · Next, we can create a new method, which returns the base theme data class, which we will then use for other contexts such as light and dark later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:16&lt;/strong&gt; · We can then import these two fonts defined in the design spec and create a merged theme, which we will then use to pass to the text theme class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:25&lt;/strong&gt; · Note that it is possible to override just a single level or groups of styles depending on the design intent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:30&lt;/strong&gt; · This package just requires internet access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:32&lt;/strong&gt; · So make sure to give the mac OS target if running the correct entitlements defining the package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:37&lt;/strong&gt; · That&apos;s it for typography.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:39&lt;/strong&gt; · Next time we run the app, we will see Roboto updated with the new correct default textiles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:44&lt;/strong&gt; · LIAM SPRADLIN: One thing that we really focused on in the design updates for Material 3 is color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:49&lt;/strong&gt; · And a lot of the changes and improvements made to the material color system were motivated by creating and preserving design intent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:56&lt;/strong&gt; · Rather than defining each individual color for each individual visual element, we established a system that generates a comprehensive and cohesive color scheme based on the relationships between colors.&lt;/p&gt;
&lt;h3&gt;Design Color + Dynamic Color&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;8:08&lt;/strong&gt; · This way, design intent for the meaning and function of different elements that can be communicated through color can be preserved throughout the process of building an interface.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:17&lt;/strong&gt; · In Pesto, a recipe screen can even be themed based on the image associated with the recipe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:22&lt;/strong&gt; · In this case, the creamy pesto pasta translating the users or the recipe writer&apos;s unique intent into a cohesive version of the design intent that you&apos;ve established and implemented.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:32&lt;/strong&gt; · RODY DAVIS: From developer&apos;s perspective, Flutter makes working with dynamic color easy thanks to a great pub package called Material Color Utilities and the extensible theme data class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:41&lt;/strong&gt; · Lets build a custom color scheme together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:45&lt;/strong&gt; · Let&apos;s add the Material Color Utilities import above our theme to get access to the HCT color system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:50&lt;/strong&gt; · The Flutter SDK already depends on this package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:53&lt;/strong&gt; · But you will still want to add this to your pub spec dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:57&lt;/strong&gt; · Let&apos;s create a method that returns a scheme that we can use to create a custom color scheme class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:01&lt;/strong&gt; · Sometimes, a design is only based on a single color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:04&lt;/strong&gt; · And for those instances, we can use a color scheme from seed, which gives us a beautiful generated color scheme using the ACT color space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:12&lt;/strong&gt; · For Basil, we have multiple brand colors that we need to use to create custom tonal groups from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:18&lt;/strong&gt; · First define a base palette that all tokens can inherit from if not overridden.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:23&lt;/strong&gt; · Next, create the tertiary tonal group and note that the palette is returning the primary palette to provide the chromatic tonal group that the design was intending.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:32&lt;/strong&gt; · Finally, create the neutral palette, which will also be used to override tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:37&lt;/strong&gt; · The color palette class comes from the Material Color Utilities and is a group of palettes that are derived from a single source color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:44&lt;/strong&gt; · Next, we will be adding tokens to the scheme class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:47&lt;/strong&gt; · You may not be familiar with the scheme class or the concept of design tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:51&lt;/strong&gt; · But the scheme class is a class in Material Color Utilities that has a one-to-one relationship with the color scheme class in Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:58&lt;/strong&gt; · All these parameters are designed tokens, which are theme values that can be passed down and inherit from in subthemes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:04&lt;/strong&gt; · Here, we&apos;re just going to go quickly through the scheme and define the tokens from those palettes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:09&lt;/strong&gt; · For each of these tokens, we pulled them from the tonal palette and get a color based on a given tone of brightness.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:14&lt;/strong&gt; · To learn more about the new token&apos;s material design, you can go to m3.material.io.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:20&lt;/strong&gt; · Next, add the overrides for background, error group, outlines, and surfaces.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:25&lt;/strong&gt; · Finally, add the overrides for surface variant and inverted colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:28&lt;/strong&gt; · Note that for the dark theme, there needs to be different tones use for these tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:33&lt;/strong&gt; · For most cases, you will not need to create a custom color scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:36&lt;/strong&gt; · But this is an example of all the things done for you when you use the color scheme from seed method.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:41&lt;/strong&gt; · We can also create an extension method on the scheme class to return a color scheme that the theme data class expects with a given brightness.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:49&lt;/strong&gt; · Now, we can add the color scheme parameter to our base method and pass the color scheme to the theme data class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:55&lt;/strong&gt; · When overriding a token such as scaffold background, sometimes, the design will use a brand color instead of a derived token in the scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:02&lt;/strong&gt; · For this token, we&apos;re checking to see if its light, brightness, and returning the brand color defined earlier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:09&lt;/strong&gt; · Finally, we can compose the complete color scheme and the theme data class in two-theme data method while making sure to pass the brightness.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:17&lt;/strong&gt; · Sometimes, we want to pull a theme from an image and have it match the main theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:21&lt;/strong&gt; · For example, a music player app can pull from the album art and change the background to match.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:28&lt;/strong&gt; · This color could be anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:29&lt;/strong&gt; · But thanks to the Material Color Utilities, we can make sure that the color will meet contrast requirements and have a color scheme that will work with our app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:37&lt;/strong&gt; · In Pesto, we can use the image on the detail screen to create a content-based theme that matches the top level theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:45&lt;/strong&gt; · To do this, we can create an image theme class, import Material Color Utilities, and the image package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:51&lt;/strong&gt; · Next, we create a method to take the image defined at the path, get the bytes, and convert them to an RGB pixel array with the image package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:59&lt;/strong&gt; · We can then implement some basic error handling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:03&lt;/strong&gt; · Then we can implement the source colors from image method that takes the pixel data, quantitizes the result, and returns them to the ranked pixels in the image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:11&lt;/strong&gt; · Now, we can create a color scheme from image method that takes the current color scheme and blends it with the primary of the top color in the image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:18&lt;/strong&gt; · We then can use that seed to generate another color scheme which we then return.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:23&lt;/strong&gt; · This blend method is important because otherwise, the color could potentially clash with the main theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:29&lt;/strong&gt; · Finally, we can use the method just to find to create a build method, which returns a FutureBuilder and creates a color scheme from the image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:36&lt;/strong&gt; · It then passes it to the theme widget, which will override the theme data in the widget subtree.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:42&lt;/strong&gt; · Once we have defined all the colors and typography, we need to create individual widget theme overrides.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:48&lt;/strong&gt; · This will help reduce the need for custom components or theming in the build method.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:53&lt;/strong&gt; · Learning how to define these overrides properly will help the application have an experience that properly reflects the design intent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:00&lt;/strong&gt; · First, let&apos;s create the TabBarTheme-- then the FloatingActionButton override.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:05&lt;/strong&gt; · Next, the NavigationRailThemeData.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:07&lt;/strong&gt; · Note that these are just applying color overrides based on the design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:12&lt;/strong&gt; · Finally, override the AppBar and ChipTheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:15&lt;/strong&gt; · There are many widgets that we did not override.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:17&lt;/strong&gt; · But it&apos;s because we&apos;re not using them yet or the default to Material 3 appear correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:21&lt;/strong&gt; · LIAM SPRADLIN: Of course, the ways that people can use your app will depend on where, when, and how they&apos;re using it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:27&lt;/strong&gt; · This is where it becomes really important to account for different input modalities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:31&lt;/strong&gt; · If a user has a keyboard, switch, mouse, touch, or even voice to interact with your app, it should be accounted for in the way your design is structured.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:40&lt;/strong&gt; · The way your layout is structured, specifically how users traverse both visually and physically from one item to the next is crucial information that&apos;s not captured in one static mockup and requires extra documentation to successfully translate into implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:54&lt;/strong&gt; · And this might change based on what device your app is running on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:58&lt;/strong&gt; · Layout density might be lower on an ambient device that&apos;s viewed from far away and has less need for touch input.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:04&lt;/strong&gt; · This will mean quicker traversal through fewer onscreen elements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:07&lt;/strong&gt; · On a phone, the density may increase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:09&lt;/strong&gt; · But things like touch targets become more important to consider.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:12&lt;/strong&gt; · Across devices, the navigation structure may change as you gain or lose screen space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:17&lt;/strong&gt; · RODY DAVIS: From a developer&apos;s perspective, we should be mindful about where the application is intended to be deployed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:23&lt;/strong&gt; · In Flutter, you can define multiple entry points depending on the target you&apos;re building for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:27&lt;/strong&gt; · Rather than building a single UI package in binary that&apos;s meant to run in all these contexts, you can compose the UI and functionality depending on which top-level app you pass to runApp.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:37&lt;/strong&gt; · In your theme data class, you can pass adaptive platform density Enum to the visual density override to create a more compact UI in these contexts where input is more precise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:48&lt;/strong&gt; · And when it comes to traversal and touch targets, it is important to remember that building a great accessible app is good for keyboard navigation and accessible touch targets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:57&lt;/strong&gt; · Flutter has a good focus traversal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:59&lt;/strong&gt; · And if you&apos;re using the default focus nodes defined in most widgets, you can test them by using the Tab and Shift Tab keys to navigate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:06&lt;/strong&gt; · Make sure to define tool tips and meaningful semantics where possible throughout the widget tree.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:11&lt;/strong&gt; · For navigation, you can use the go router package to split up the routes into files and have fine-grained control with the new Navigator 2.0 class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:19&lt;/strong&gt; · This is what the Flutter app looks like running without our theme applied.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:23&lt;/strong&gt; · But when passing the theme to the Material app, we get a beautiful Basil Material 3 theme in Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:28&lt;/strong&gt; · The theme data class is very powerful and allows us to create truly custom themes that match the brand or design spec handed off to the developer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:36&lt;/strong&gt; · Sometimes, a theme for a widget is not available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:39&lt;/strong&gt; · And for that, we can use extensions and custom widgets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:42&lt;/strong&gt; · LIAM SPRADLIN: If you still find the concept of building a custom theme challenging or just need somewhere to get started with your design, check out the Material Theme Builder and export your theme for Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:52&lt;/strong&gt; · You can also learn more about Material 3 and Material.io.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:55&lt;/strong&gt; · Thanks for watching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:57&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Flutter as a Service (Webinar)</title><link>https://rodydavis.com/videos/flutter/flutter-as-a-service-webinar/</link><guid isPermaLink="true">https://rodydavis.com/videos/flutter/flutter-as-a-service-webinar/</guid><description>Join Rody Davis and Frederik Schweiger for a webinar exploring how to make money using Flutter as a Service.</description><pubDate>Tue, 22 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Flutter as a Service (Webinar)&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;b8ftugJvgGY&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Rody Davis and Frederik Schweiger speak as part of an online webinar for making money with Flutter as a service.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · thank you everybody for signing up today this is made possible thanks to code magic Thank You Marie and Martin both in the chat we are excited about taking on this subject as flutters of service and I thought like who better to do it with and Frederick so this is going to be a quite fun yeah it&apos;s gonna be pretty&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:22&lt;/strong&gt; · interesting to say the least we have some really exciting topics plan so just like a general overview of what we&apos;re going to be doing today and going over we have we&apos;re gonna talk about our backgrounds first to kind of get some credibility for those that doubt and then we&apos;re gonna have we&apos;re gonna start with Frederick&apos;s talk we&apos;ll have a short Q&amp;amp;A after that and then we&apos;ll jump into my talk we&apos;ll have another Q&amp;amp;A and then for those that are around we&apos;ll have some drawing and prizes for those that are asking good questions so both me and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:52&lt;/strong&gt; · Frederick have have some special stuff to give away I will be giving out some promo codes of the widget studio for both Android iOS and Mac OS and I think Frederick don&apos;t you have some stuff too yeah I will have I will also send out basically I think Marie will send out some emails after this event to everyone who attended and they will also contain some kind of promo codes and other maybe smaller gifts so yeah stay tuned it&apos;s kind of a surprise yeah yeah yeah I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23&lt;/strong&gt; · think it&apos;s really good for everyone who&apos;s attending and yeah lazy to recording right yeah exactly all right so so Frederick can you tell me a little bit about yourself yeah sure first of all things also from my side and thanks for having me yeah my name is Frederick I&apos;ve been six&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:49&lt;/strong&gt; · six years do something like what while I was attending high school I got into mobile development and then had my first job also developing Android apps basically after I finished University I was employed for exactly half a year or something and then I realized I become a fluttered freelancer and probably was one of the first in Germany in the European area with that yeah and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:15&lt;/strong&gt; · then since then I just what self-employed basically building a flutter it&apos;s yeah awesome really enjoy it so yeah so what I guess we can just go ahead and jump into it but so like well what are you gonna be talking about and then whenever it whenever you&apos;re ready you can definitely go ahead and start and you go ahead and give you controls as well yeah maybe yeah we just have like a short overview&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · I think yeah the topic I&apos;m going to cover is building products your users actually one I think that&apos;s kind of a broad topic it could mean for those who&apos;ve been around for some time maybe you follow the discussion and yeah it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:03&lt;/strong&gt; · more covering the whole area of building side projects many profitable profitable side projects and maybe turning them later into their own into your own business having something like solo founded or founded with two people like an indie business something I think this is like the bigger picture for today so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:24&lt;/strong&gt; · yeah that&apos;s that&apos;s my kind of talk and I just try to explain it a little bit just give you some hints I mean I also just started out I&apos;ve been on a few smaller projects myself like also side projects and then small startups which then at some point failed some were successful with like small things and I just try to put in some insights that I have gained&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:46&lt;/strong&gt; · and also please don&apos;t take them that serious because it&apos;s just like my my only based on my own experience I think everyone has to make their own you know but if you can get yeah it would be great right okay yeah yeah cool maybe row do you want to already say something what yeah I&apos;ll give a quick snippet and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:08&lt;/strong&gt; · then cuz it&apos;s gonna be related to yours but then you can go ahead and start yours just so so what I&apos;m going to be talking about in mind is basically a follow up to kind of flutters of service in general whereas yours is a case study I want to be like be able to like mirror a lot of that kind of stuff I want to show like examples on how can take an idea or a future idea and be&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:28&lt;/strong&gt; · able to monetize and create money in the future I want to show everyone that like how many you know options are possible and some things that you may not think about when creating applications and like you know if you&apos;re someone who is a UI designer for example like oh you can make money too and I can show you ways that you no matter your skill set where you can find your niche and fit right in so yeah but I&apos;m really looking forward to giving more about wire - nice&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:57&lt;/strong&gt; · ok then I would say I would just I just it&apos;s just an invitation that popped up that some people are in the waiting room I just yeah you should see admit the only one person can be the host so ok should I admit all of them that 3x yeah you can you can you can click it admit all and mutes by default so ok until until you share with me again ok ok let&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:28&lt;/strong&gt; · me just share my screen see you and make sure to check the chat too because you can um show a portion of it - yeah because currently I&apos;m just I think I&apos;m just sharing my doors all right you got it ok so everyone should just see the big slide right right thumbs up yep see you&apos;re good good nice cool yes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:54&lt;/strong&gt; · as I already said my talk for today this called building products your users actually want or yeah what I learned while building why - I own for those who don&apos;t know what Wireless IO currently is it&apos;s like a feedback tool for flutter reps and interactive feedback to flutter reps where your users can give in that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:17&lt;/strong&gt; · feedback without leaving the app and basically it just captures your screen has like a very not very but like kind of fancy you write with so it&apos;s hopefully it&apos;s fun for the user as well to engage with the developers or with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:32&lt;/strong&gt; · with the team behind there and then give you productive feedback on how to improve yeah so that&apos;s what we&apos;re issues but first of all I want to again emphasize this is my disclaimer everything I&apos;m telling right now if you follow this advice and whatever your house catches fire or something it&apos;s not my responsibility it&apos;s just like for entertainment purposes and for you too many you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:00&lt;/strong&gt; · get some more insights from someone who did something similar right okay so you&apos;re probably here because you&apos;re interested in starting a business or starting a side project in the flutter space right so everything here usually it starts with an idea like every business idea whatever however I don&apos;t really like the word&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:24&lt;/strong&gt; · idea here because it doesn&apos;t really resonate with me I always have to think about those big Silicon Valley kind of startups which have like a lot of money and funding as well and that&apos;s actually what I don&apos;t want to target in this talk especially like target for developers or solopreneurs people just want to get out there idea because there was also when I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:47&lt;/strong&gt; · was attending university they were and I was younger I had like also an Android sticker on my laptop as I said I was an Android developer break them and then also two students approached me to Business Economics students and saying yeah we have like the biggest idea right and we want to share with you that&apos;s always like this kind of high gear that will change the world and they said you have to sign an NDA and stuff in order to know the ideas said okay as some&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:13&lt;/strong&gt; · experiences there was a gap signing there just let me know what it is they&apos;re like we&apos;re going to go to the group owning it and as you might know component games they were like a huge deal they then Groupon was huge and they were probably in there at the market like four years right and Google offered them I think a few million make sure you get the whole product and they did kinda know those guys they they just saw they can copy the idea and and the end I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:37&lt;/strong&gt; · should just get like 10% of shares for basically building the whole text second building their app rain which was not a good offer so what I&apos;d like to you to do here is nothing of an idea but think of kind of a pain point right the pain point that you as a developer or or maybe even an entrepreneur or as someone in everyday life experiences yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:02&lt;/strong&gt; · because if you were having this kind of problem at least one person yeah actually has this problem and that&apos;s already a huge plus because then you&apos;re you&apos;re kind of solving a real-world problem so that&apos;s also already a check mark they&apos;re also the probability of other others having the same problem that is kind of high because there are a lot of other developers around here basically everyone probably in this meeting and if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:28&lt;/strong&gt; · you have a specific problem regarding flutter that&apos;s very likely that if you share it with others they would really appreciate it or maybe even pay money for it if you would save them some time right and since flutter is kind of neat as well yeah there&apos;s still a big you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:45&lt;/strong&gt; · know piece of the cake untouched and so that&apos;s why we&apos;re actually having this webinar right now and I think rody will also elaborate on that further so I want to make a short discretion here and you know talk to you about how to make 1 million this is not like some kind of con series I want to you know make your CFD traders and let&apos;s join the whatsapp group no it&apos;s not it&apos;s just like showing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:11&lt;/strong&gt; · you actually what you would need to do in order to make 1 million revenue in a year so it&apos;s basically five thousand people buying a two hundred dollar product for 1,000 people 1k product and so on right so basically you would just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:27&lt;/strong&gt; · need ten people paying you 20 pane and you would have like a 1 million 50 people paying 20k and you would have 1 million dollar revenue right the thing is we&apos;re talking about software here and usually in the software as a service space it&apos;s fine you know it&apos;s quite usual to to have sell something like subscriptions so if we turn the page around and have a look at systems in prices it would just take 5,000 people who subscribe for 17 dollars a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:59&lt;/strong&gt; · month to actually make 1 million revenue I mean take those please go on and take this numbers with a grain of salt here because there&apos;s like all happy day cases when we&apos;re thinking about those people being subscribed for a year right in order to hit this million and then you have to probably also pay taxes and stuff so the whole mini an idea is kind of just to change your mindset right or to line your mindset and yeah in the end you could also just have let&apos;s say 500 people paying 107 60 $70 per month to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:32&lt;/strong&gt; · also make this million or ten people a day whatever and that&apos;s actually not too far away from b2b prices right if you if you actually delivering a high quality service that&apos;s very niche for a company that saves a lot of money well you know they&apos;re using your service or in the end you can also just have one person Y in your startup so Martin if you&apos;re in this call and you&apos;re listening to this please make sure to stay in its call so we can discuss the details you know the quiet&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:02&lt;/strong&gt; · but you know whatever also a very interesting fact here is that there are two million active cutter developers right now probably even more at this very moment and that would mean you only need 0.0025 percent who would actually&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:20&lt;/strong&gt; · subscribe to your product again for a year right to make this 1 million and even if you would say double this number and say this 0.05 percent it&apos;s still not very high so coming back to the pain point I was talking about was I usually&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:38&lt;/strong&gt; · this is something that my users don&apos;t want I don&apos;t know about you but I&apos;m a developer I like playing around with new kind of type stuff so you know i when i finished uni i just still things that i enjoyed you know building would you lead no one else would pay any of any cent&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:54&lt;/strong&gt; · for like where using control level an app or something or I once build an app which you could stick them to the Nylund my sister&apos;s night lamp and if you would go to sleep like a mosquito sound would start to play and if you would like turn on the lights to search for the mosquito which is stopped right I mean this is the those are things that that are fun but usually if you have this kind of market market groups you market for let&apos;s say underaged people like you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:23&lt;/strong&gt; · buying this mosquito noise or sound net usually they also don&apos;t have that much money to pay for you hurry up so you want to target other developers or especially I included in the b2b market in which we are right now so the idea behind I&apos;m building something that my users don&apos;t really want was user&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:46&lt;/strong&gt; · feedback tool that doesn&apos;t suck so that was the idea I came up for myself and so yeah I just went out and basically build the prototype for it and on the 25th October 2009 you can see the Treaty here and I just put it out I just want to check how is the resonance and what people like it or not and I actually got a really positive feedback and from from&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:12&lt;/strong&gt; · the community as you can see it&apos;s like nearly four hundred four hundred likes and one hundred retweets and even tim smith from from clutter from the PM pro flutter at Google right he replied when I said that he&apos;s loving it so I mean what kind of verification&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:27&lt;/strong&gt; · can you get like if it&apos;s not the product measure of the product itself saying that it&apos;s actually quite good so yeah I think I found my niche and I also verified we are here and I got in touch with the committee so that was actually pretty great and I got kind of excited a little bit overexcited so I started building the first MVP I don&apos;t know if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:48&lt;/strong&gt; · everyone knows what an MVP is sends usually for a Minimum Viable Product and that&apos;s a product that just offers enough features that people would actually start using it right and that that you can try out the idea behind it and that it just works that it barely works there&apos;s no like fancy features around maybe not even the sophisticate design so yeah I went out and started&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:12&lt;/strong&gt; · building it came up with this I don&apos;t know some people still remember it it was called snap feed it wasn&apos;t even called a wire - thank them and as you can see it was kind of super euphoric because as you can see everyone who wanted to implement it a thousand of the projects so you had this beautiful project chooser because you would have thousands of maps obviously and yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:36&lt;/strong&gt; · this was basically the 2d detailed view of the whole product so if someone sent the feedback it would come into into the into the yet do this list and then you can see the date you can set the status if it was new plan the progress closer was basically like you know yeah planning solution yeah right internal to focus customer management and yeah back&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:03&lt;/strong&gt; · then I actually put out a landing page what you always read on on the Internet and I collected email addresses and I actually had quite a lot of email addresses but actual users of this app have been like 15 to 20 only 15 to 20 years it&apos;s and all those 50 to 20 years is no one really needed the status no&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:22&lt;/strong&gt; · one needed like the sorting functionality no one used the owner which it could assign so you could even invite people to your project but no one would use it so all the stuff was actually not really used by those people but this doesn&apos;t stop here I got so excited building my empathy that I even added this beautiful setting screen with all the integrations you could even like choose a branding upload the logo one and change the phone remotely obviously right so you didn&apos;t have to do it on the client side you could change it remotely and even integrate edit integrations&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:53&lt;/strong&gt; · email slack and for sure test magic coming soon and secure so not many people were actually using slack back then although this is like right off my best feature and only one person you see here and that was me in my example project in order to set up the Xavier API so yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:15&lt;/strong&gt; · that was me being done building the MVP which I call the maximum viable product because it was basically a full-blown product already and not something very simple that I could just test and throw into the market right so please if I learned something don&apos;t build a maximum buy a good product and also don&apos;t spend more than this initial&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:39&lt;/strong&gt; · phase right and also please please go ahead and talk to your users that&apos;s actually a very very important thing which I will also liberate later on this talk the next step for me in this in the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:54&lt;/strong&gt; · whole journey was actually to rebuild the whole MVP with all the feedback I got from the community again here you know sometimes my developer and OCD kicks them you know I just have to rewrite the code I think we all know that so you wrote write some code and it&apos;s actually not as great as you thought like half a year later right I&apos;m one of those guys yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:22&lt;/strong&gt; · please don&apos;t do that don&apos;t don&apos;t ever rewrite your whole project after you got a product which actually works so however the good thing about snappy was that I was building the whole sniffing SDK and the public so as you can see that it was public and open source project on on github and you can see&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:43&lt;/strong&gt; · like some open issues some closed issues they&apos;re not bad many because it wasn&apos;t actually used by so many people but as you can see they&apos;re already like a lot of interesting features right there was a question how to handle localization as an example someone wanted to cut some strings someone what wanted to support like more theming or more customization options so all of those feature requests they had nothing to do with the front and then I was building right so even continuing that having a different channel this is basically my email my inbox for wire - and there&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:16&lt;/strong&gt; · only a few of the things that that reach me over a few different channels and as you can see you here people are super eager to actually give you feedback if they if you build something that like easier so if you&apos;ve come up with a problem that you think is is a good good&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:34&lt;/strong&gt; · idea to be solved then just go out and try it out and people will reach out to you and they will come up with cool ideas you would never thought of that you wants to integrate into this platform right so I came and I took all those ideas and which I collected over all the different the different&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:53&lt;/strong&gt; · platforms and channels and then I put them onto a yet Trello board and I came up with some yeah smaller ideas and and put some my tears in the backlog and stuff like that the usual way you do when you doing like to start right so I can&apos;t collected them here and then I started working on them so what I&apos;ve learned from this phase was actually it&apos;s super important always get in touch with the users and then trying to only build requested features or features that are requested by someone from the community also which thing that I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:26&lt;/strong&gt; · learned I think a very important and essential part is ask for help and also help others so that&apos;s that&apos;s one of the most important things you can do if you if you start building your own business is get in touch with your community as early as possible try to solve really solve someone&apos;s problem because then it&apos;s not your problem because it&apos;s the form of two people already and this is how it grows again again you want to get this inside swirl rain and then again&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:55&lt;/strong&gt; · please don&apos;t rewrite your entire code base so in the end Wireless was born and as some of you may know this is how it currently looks this is this is the website how it looks today and this is a picture of the current console and the interesting thing about the console here is that it looks completely different than the console on snap bean and that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:20&lt;/strong&gt; · actually because I just build the features committee retrogressive because if you can see this year this is like a github kind of search I would have never built this if no one asked me to write so I build this because it was an issue on github action and then someone asked for having labels to organize the stuff because labels are when sophisticated than just having those predefined statuses and that you can set but nobody used because do you want to search their own like create their own workflow and they don&apos;t want to adapt your workflow and then there&apos;s a close&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:53&lt;/strong&gt; · button way you can just close today if it&apos;s done so it&apos;s not shown on the on the initial page load and then you have an options button we can actually delete stuff there was also a request I got from from a small start-up in Europe and because in order to be compliant with GTR they also hinted me and said okay we have to be able to actually delete feedback and even if you&apos;re not from Europe or if you from from like us or Africa or wherever you don&apos;t have to deal with GPRS at first class citizen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:22&lt;/strong&gt; · right you get this feedback and you want to support it and that&apos;s what your users actually tell you and even going on on integrations and members if you have a quick look at you at the new member section and that&apos;s also something that completely changed although you didn&apos;t see the old member section of snappy but there&apos;s something that changed because people wanted to have different roles like administrator&apos;s attitudes viewers I just build up because people asked with you and then also the possibility to edit or remove members with those kind of rules also the feature that you can remove yourself also having something&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:55&lt;/strong&gt; · like pending invites that you can revoke invites that you accidentally sent to the wrong email stuff like that you can do now and that&apos;s just like because someone because of this feature so again we drone them start small and let your users guide you always get to know your users and please keep iterating and ship&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:15&lt;/strong&gt; · features this is also something that I learned be Hardware the hard way because people always say you have to ship you have to ship you have to ship it sounds so silly but it&apos;s actually true because also what I&apos;ve learned about entrepreneurship the hard way is everyone talks about an exciting way if you see healing must beg you launching SpaceX Rockets you know - Tamar&apos;s whatever right all the kind of stuff it looks so exciting but it&apos;s actually not that exciting in everyday life it&apos;s still like a tough job to do it it&apos;s it&apos;s a decision you have to have every day and that&apos;s why I asked me to just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:47&lt;/strong&gt; · keep iterating on small features so you can ship something then you get feedback from users again and that motivates you a lot when I build the feature like the github search feature and and then the labels I did this like in one in one thing right and one more flow the problem there was while doing doing this big search and doing this kind of like complicated or big feature labels I had to deal with so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:15&lt;/strong&gt; · many things that I took me like three or four weeks or something nearly until I basically shipped a new feature until I get a new feedback from my customers regarding those features and it&apos;s also like kind of demotivating if you don&apos;t get updates or if you don&apos;t share stuff or if you don&apos;t have any visible progress so please go on and ship small features and then ask to use what do they think you can improve all the stuff the next thing I would want to shortly talk about is actually doing things that don&apos;t scale and I think I&apos;ve stolen that from a lot of other people or already&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:47&lt;/strong&gt; · like doing building startups and and so on just my experience here it&apos;s super important or valuable if you if you do things like sending out stickers to people Rodya look love letter that is this on the way currently to the USA right and yeah just send out stickers&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:09&lt;/strong&gt; · start with something like that say thank you to those people that actually supported you try to get in contact with them right and for that I&apos;m saying put yourself out there go on Twitter it&apos;s a huge community it&apos;s it&apos;s a nice community you can learn so much from so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:26&lt;/strong&gt; · many talented people like I can learn from roadie like it can learn from I don&apos;t know like a lot of talented people out there they&apos;re way more talented in a lot of areas than I am if you just go out there and talk to them and ask them what you can do better they most likely will help you if you&apos;re if you&apos;re asking a nice way then for sure print stickers and send letters right I mean you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:48&lt;/strong&gt; · discuss that kind of thing but also very important is yeah building relationships that you&apos;re actively it can be taking part in and what we do right now like webinars asking people for help maybe even write writing long emails there was one timed putting&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:05&lt;/strong&gt; · two clients I wrote like two or three denied four-page long emails back and forth and they were so excited about the product because they&apos;re the clients loving it so much and this is actually what you want you want to being in touch with your first ten users and who everything what they want because if you do that if you saw those ten problems you&apos;re solving probably a lot of problems more like many many more people will like likely use you as your product and then there&apos;s one thing I just wanted to mention on the side like before you know I wrap up the whole thing is also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:38&lt;/strong&gt; · go on an automate and what we discussed before I actually started this talk there are so many already tools out there like code magic as a CI rank it can make your life so much easier it&apos;s already been there you don&apos;t have to prove yourself especially if you start your solo business that you can build your own CI why would you it just takes take your time if there&apos;s like a tool for automating I don&apos;t know like getting user feedback right and then maybe use that one because in the end it&apos;s just like you just trade your time or Y and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:10&lt;/strong&gt; · the end your money for a product and if you think about if I would build a feature or something or a service that would actually help me like a customer relations tool or a CI tool or something if you think about how much work you can get done on your own product or on your own project if you think about how much it would cost you like let&apos;s say 50 years or 100 bucks a month and if this feature would take you ten hours to implement then probably those ten hours or like even 20 hours for something right you could use this time your time&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:43&lt;/strong&gt; · is probably more valuable than those one hard of 50 bucks right a month so if you&apos;re like thinking that way it most of the time makes sense to especially in the early stage use cloud providers use third-party services just get your thing out there and that&apos;s yeah basically it already that was my short like intro on what I can tell you if you have any questions or specific questions feel free to ask now I would have to check actually and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:12&lt;/strong&gt; · arrange the the whole shet thing here there&apos;s a lot of questions made but yeah that that was it for now from my side and I think I&apos;m going to switch it over back to roadie so you can maybe handle people who want to join and I just saw on certain questions is already sounds good ok cool so let me check how can how can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:49&lt;/strong&gt; · I make you actually the host again roadie ah I think you should be the host no all right thank you okay hear me yes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:06&lt;/strong&gt; · cool well thank you so much yeah that was a that was a really exciting take on wire - like I&apos;ve been you know I&apos;ve been following you know this Snapseed and stuff like that it&apos;s been it&apos;s been interesting to see it like evolve and kind of like grow into like you&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:23&lt;/strong&gt; · finding your audience you&apos;re reacting you&apos;re evolving and it&apos;s just like that cycle you just keep you keep cycling it up because like you&apos;re never gonna be like in a perfect state you&apos;re not gonna have ever arrived with wire - and so I think it&apos;s really cool to to see the journey and also like a tangible journey for people to see like it&apos;s not so like cut and dry it&apos;s not like okay if I do this this this and this then I&apos;m gonna have a perfect product and then just now I get money from here on out it&apos;s like it&apos;s a it&apos;s all about the vision and the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:52&lt;/strong&gt; · the the passion that takes you know as an entrepreneur we&apos;re also like you know supporting an application and releasing it so we had some pretty interesting questions in the chat so first of all what is your take on like flutter web like how do you feel as if for maybe either using it as a you know something&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:12&lt;/strong&gt; · for this product or just in general flutter web for applications because I think originally it wasn&apos;t snap see like view yeah yeah yeah actually stem feed was you and also the current console is building to be honest just because of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:29&lt;/strong&gt; · the reason that when I actually tried it out a month or two or three months ago you have to think about that right when I started implementing this stuff flutter web still have some kind of issues with actually you know selecting text and all that kind of stuff what I thought is kind of interesting if you just want to copy the contents of a feedback or share stuff or copy an ID because I don&apos;t know if you know you can also send user IDs or emails and stuff&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:58&lt;/strong&gt; · right or touch some info to white-ish and then you can basically assign this or see what kind of user basically is sound what kind of feedback and to make this just better handleable right or yeah I don&apos;t know yeah yeah I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:17&lt;/strong&gt; · decided to actually go with who also because there&apos;s like a lot of images for sure and the image performance was not that great when I actually tried it out right I mean it gets better and better yes I mean maybe if I would start now&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:33&lt;/strong&gt; · maybe I&apos;m gonna do it if I&apos;m surely targeting web though that&apos;s the thing is like like I feel like if you&apos;re targeting multiple platforms across platform first that&apos;s a way different question than if you&apos;re only building web application and functions as a service and ever since it goes around there like that actually the SDK is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:55&lt;/strong&gt; · written in Dart right so you could basically write so it may would make some sense for sure but also the fact that the navigator is not that great yet right with sending and sharing Lee playing with this hashtag and since you can basically invite people as well I mean they&apos;re workarounds so you can make your basically write web service acting the URL and then doing the redirects and stuff like that so you can hack it but I thought for for a sophisticated platform was good to go yeah and they&apos;re also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:28&lt;/strong&gt; · like great CSS frameworks out there that&apos;s better support and supporting you doing a good UI especially for dashboards and stuff of course yeah and that&apos;s the thing is you&apos;re leveraging what&apos;s available which is kind of a maybe a predecessor to what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:46&lt;/strong&gt; · we&apos;re gonna be talking about later or a foreshadowing but you know it&apos;s like you know this is where the developers can step in you know flutter web is a new platform like you may think it&apos;s not ready but this is also where you can come and say okay here&apos;s a need in the market let me find a way to provide for that market so just an example of just a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:05&lt;/strong&gt; · real-world example if you know there&apos;s flutter web there&apos;s many things needed right now on the web in regards to you know dashboards for different kind of charting libraries you need layout libraries for things like you know something as simple as like layout grids and stuff that people are used to on the web things like templates like you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:23&lt;/strong&gt; · something as simple as like a wordpress template but the same kind of idea for flutter web applications you want to have like this kind of theming that goes beyond material for some applications or just pre-built kind of things so this is all places that developers can step in and be like okay I&apos;m gonna actually offer this because it&apos;s not gonna be there until we create it sure and I think that there could be I think you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:47&lt;/strong&gt; · you&apos;re also liberating on this in your talk right but I mean I think when we were when we met in New York we said in this co-working coffee and I think within 10 minutes that&apos;s actually a good&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:04&lt;/strong&gt; · idea and this is definitely a market for this in the future so in the end it&apos;s just up to the people to see what actually also is a bit of fun for them to implement and also like a pain point because I think especially what I can&apos;t emphasize it enough it&apos;s a it&apos;s a tough road right if you start start building&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:22&lt;/strong&gt; · something yourself it&apos;s going to be you will you know will have like dip or something low and you just like no one&apos;s going to use that I always have I&apos;ll have that all the time right or you think okay this is why it is you just can&apos;t give user feedback is this like so good you know and then I need someone from outside Emma&apos;s so cool like when I show it to my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:42&lt;/strong&gt; · clients they&apos;re always like they can&apos;t grasp you know because it&apos;s like so crazy when it like makes the app small that stuff and then those are the moments but you&apos;re like sure like yeah that&apos;s really cool and and actually making someone else happy or making their life easier is also a big part at least for me to to build something so don&apos;t underestimate that that you should always you know go with small steps and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:04&lt;/strong&gt; · rollout stuff and just keep going yeah I also don&apos;t believe in the overnight like kind of success right so it&apos;s oh it doesn&apos;t exist yeah yeah yeah it&apos;s a lot of work behind the scenes literally all the time right yeah well and also we had a good question to check the chat how are you finding maintaining your sdk like what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:26&lt;/strong&gt; · kind of issues have you ran into not specifically but like how are you dealing with like maintaining this as a small team / one person yeah actually n so that&apos;s a really good that&apos;s a really good question because I think they&apos;re also like to people who really helped me a lot and with wire - recently it was Dominic and Nash from from the community because I actually started the whole thing by myself but as you can imagine building an sdk then building a back-end&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:53&lt;/strong&gt; · and building it from then as one person is kind of tough that sometimes right and you just move slower than you might want to write so actually I just delivered the whole SDK upfront and then I just basically asked two people those&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:10&lt;/strong&gt; · two people to help me and they did a really great job until now right and chrony I&apos;m also looking forward to actually rewrite some parts of of the SDK to make it more sophisticated again because some people told me things that aren&apos;t currently possible although I already tried to to make work in in most scenarios but yeah I&apos;m just trying to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:34&lt;/strong&gt; · build the stuff in the public there as well so if you&apos;re on github make it open source sometimes you people are so so interested in your project even if it&apos;s like open source but you know yes it&apos;s like a paper I mean there&apos;s a feed free plan as well feel free to go and check it&apos;s free for developers and it will always will be but even then there are people who are excited because they really like this idea and and how it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:02&lt;/strong&gt; · works and it actually does the job so well so they would even come in their time and help you out with that that&apos;s what I was saying and referring to build your community and you know just be nice to the people and they would be nice to you it&apos;s always like that kind of stuff right and so yeah it&apos;s just like an open-source project I&apos;m working on it if I have time for it or you know then I just hired two freelancers like Dominic&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:28&lt;/strong&gt; · and ash to help out at some time at some point to just get out new features yeah I hope I can find more time actually in the future and I I think I will to add some really really cool new features that I that I think are yet worth implementing and adding like details so yeah it&apos;s a lot work wait yeah so as we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:53&lt;/strong&gt; · asked most people what&apos;s your work-life balance like how do you manage like being like entrepreneur but also you know staying productive and being on the never-ending Twitter I think that&apos;s a good question I could ask you because I think you&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:10&lt;/strong&gt; · you&apos;re pushing out way more that I do I feel like a slow slow torches you know just like very slowly and then you come up with like this widget builder you come up with like thousands new libraries and so that&apos;s actually a good question because sometimes you go on Twitter and you see so many people building so many things and that I also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:33&lt;/strong&gt; · say I just I&apos;m I&apos;m for sometimes I&apos;m just not active on Twitter I just don&apos;t check it at all because you know you just somehow kind of get demotivated right at some point because you&apos;re like I&apos;m still working on this feature for like two weeks and there&apos;s like two guys coming up with like a whole new state for the German solution right that time yeah yeah I don&apos;t know so that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:55&lt;/strong&gt; · actually a tough question to ask and I think everyone has to deal with it in their own way all right I have usually tried to do also some sports and don&apos;t put too much myself definitely tried to go the way of also having it as a side project right so I also have another business actually where I&apos;m rebuilding an app right for for some telecom so that&apos;s where I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:20&lt;/strong&gt; · actually make money because I right now I don&apos;t make any one little wire dish because I just want to build it and get it out and get it in people&apos;s hands so we&apos;re sure people though I&apos;m I don&apos;t have that pressure and I think that&apos;s an important part of not burning out too fast right to have this pressure to deliver something because I think also good stuff takes some time especially if you want to involve the community and actually get those ideas and yeah for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:49&lt;/strong&gt; · sure I mean I know for me like I have plenty of things that like I keep busy with outside of development like I also was a musician so it&apos;s like you know music is such a big part of my life I go hiking and I do a lot of VR so it&apos;s like but I also do a lot of procrastination so like yeah I may be really busy but it&apos;s like I&apos;ll wait really long and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:11&lt;/strong&gt; · tilde do it a project and I&apos;ll get it done in like 20 minutes but it&apos;s like it seems impressive but I&apos;m also like mulling it over in the back of my head creating these things and they&apos;re all like kind of forming and then yeah just do it like the implementation is easy part it&apos;s the it&apos;s the edge cases that keep me up at night and I&apos;m most a big procrastinator here so what also helped&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:35&lt;/strong&gt; · a lot was actually to not put too much pressure on yourself right that&apos;s what I said well it&apos;s then like I don&apos;t have to ship this like tomorrow right I mean it&apos;s um you have to have some soft deadlines for sure otherwise you never you never sure for sure but you also even artificial ones yes you shouldn&apos;t you shouldn&apos;t like rely on this or there&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40:56&lt;/strong&gt; · would be the best way not to rely on this every day if your everyday job and you have to succeed because in the end right you know the one who is deciding if the market actually wants this idea or not but if you&apos;re like very niche or very special it&apos;s just your your problem right and then you can storm something and until let&apos;s say you realize that the idea has no potential you should discard everything like weeks right so you have to invest three months four months into actually developing and putting it out and I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:27&lt;/strong&gt; · think this can be kind of tiring as well if you do this like multiple times up region offer each other right so if you fail once everyone&apos;s just said I just keep on iterating and failing but they don&apos;t take into account that it actually also tires your right maybe you can&apos;t have like this high a motivation which I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41:45&lt;/strong&gt; · actually have which I am yeah which I would like to have right for new projects and then it&apos;s more like it becomes a strain of I have to do this now and if it becomes turns into this I have to do this the procrastination wants to gets also bigger and you&apos;re like yeah no so Before we jump into my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:12&lt;/strong&gt; · talk I wanted to real quick take a photo of everyone so if you are on the call please go ahead and like you know activate your video let&apos;s all get a screen shot Before we jump in just in case we can see like you know who&apos;s on before we transition because you know people drop off and then while we&apos;re doing that Frederick can answer for me real quick what do you think about firebase is a back-end yeah for everybody I actually&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42:37&lt;/strong&gt; · think this is a great solution if you&apos;re yeah if you&apos;re building just like a flood maybe yeah flutter old me up I think it depends right if your ends on your you want to let&apos;s say like don&apos;t want to build a back end if you just want to rely on firebase and firebase functions to handle some kind of micro transactions or whatever I think it&apos;s definitely fine but also think about that you somehow lock yourself in a bit although I think the services they are offering are great but if you want to do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:08&lt;/strong&gt; · some more let&apos;s say sophisticated things some it&apos;s a fine like having naoki I mean we&apos;re just running all the time I mean cron jobs all that kind of stuff you can do it up to a specific degree right and but we all know at one point it just gets a bit happy right I think yeah definitely for sure you can always start with firebase when you&apos;re starting out in that because you can always migrate the data right it&apos;s exactly if you structure the layers&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43:35&lt;/strong&gt; · exactly right so you can always move the day I don&apos;t think it&apos;s that big of a problem and given if you use firebase health education you can even export all the data as well or just keep on using firebase off I actually also use five it off because I think it&apos;s it&apos;s great yeah a good abstraction layer - yeah exactly right and yeah so I think it really depends on your needs if it&apos;s just basically if you&apos;re just using the standard functionality and especially firestore office you&apos;re in I&apos;m great&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:06&lt;/strong&gt; · like search then it&apos;s then it&apos;s great but if you need to have like a more sophisticated search right like let&apos;s say something with geo geo stuff like yeah yeah yeah points I mean you can use geo hatches and stuff but they&apos;re like they have the limits right so it&apos;s all about the data yeah I think it depends and then there&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:30&lt;/strong&gt; · also different different technologies for that right some some things there there&apos;s the real-time database that would make more sense because I think they just bill you for for the transportation storage and the firestore&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44:45&lt;/strong&gt; · actually counts reads and writes so it&apos;s also different product so you have to think about is it more do I store like a little bit of data so the trends very fair rate would be low and yeah I have a lot of like real times yeah probably go with real-time database instead of the firestore right but if I also got all of GCP - so it&apos;s like you can do whatever you want which by the way there&apos;s plenty of shy people so go ahead and turn your videos for all the introverts as well so okay and we&apos;ll have another Q&amp;amp;A at the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:24&lt;/strong&gt; · end too as well so don&apos;t feel like if you if you need to ask your question again you can alright so I think this is everyone last chance if you want to be making history so go ahead all right let&apos;s see that&apos;s it all right redo it all right we&apos;re gonna count down on ten ten nine eight seven six five four three two&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;45:50&lt;/strong&gt; · one nice thank you everyone okay so yeah sweet yeah I just it&apos;s good to get a kind of a good vibe Before we jump her in because um usually the people at the n are everyone that was here so let&apos;s go ahead and I&apos;m gonna go&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:09&lt;/strong&gt; · into mind but um so I just wanted to kind of reiterate like a transition to be what I&apos;ll be talking about so whereas yours was more of a case study mine is gonna be more talking about like you know kind of examples on how to approach these situations because I know that it can be really daunting especially as a junior developer when you&apos;re like trying to make your place in the developer community it can be kind of scary to be like okay well these are the things I&apos;m good at these are the things I want to do and sometimes they don&apos;t overlap and also like oh I&apos;m good at building apps&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;46:42&lt;/strong&gt; · but I don&apos;t know where to build them you know it&apos;s like it&apos;s like you know I remember when I was first starting like I was like oh great I can&apos;t build an app but I don&apos;t know who to build it for you know it was like that you know you can do something on dribble or you can do something whatever it&apos;s like then it&apos;s just like taking that too like okay well how do I actually make money on this how do I take this from a hobby to a full-time job how do I change careers you know there&apos;s the whole like transitionary period that you face as a junior developer or even just for an&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:10&lt;/strong&gt; · experienced developer changing fields into a new technology so but today we&apos;re gonna be talking about specifically flutter as a service and how to do it in regards to you know making money you know full-time so I&apos;m gonna go ahead and start my presentation I&apos;m sorry for the people that will not be able to be admitted at the time so let&apos;s go ahead and do this and I will answer that question at the end let&apos;s go ahead and jump in you share my screen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;47:50&lt;/strong&gt; · okay all right can you see my screen Fredrik yes okay cool all right and thanks again to everyone joining this is really exciting this is kind of a topic I&apos;ve wanted to talk about for a while but you know you kind&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:07&lt;/strong&gt; · of got away from flutter to kind of become more popular he had to like a catch-22 but yeah I&apos;m really excited because there&apos;s a lot of things that I&apos;ve been implementing on my own and things that you can as well and I know Fredrik has that it&apos;s an untapped market&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:23&lt;/strong&gt; · and we were just dipping our toes in and there&apos;s a lot of room for everyone so as you saw like in Frederick&apos;s talk about how how much it takes to get a million like I mean that is just right on the nail and a lot of people don&apos;t realize that you don&apos;t have to have well yeah if you&apos;re only doing ads you need a millions of people but if you are willing to you know do paid upfront or subscription or anything like that you can easily like you know make it with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;48:49&lt;/strong&gt; · just like maybe less than 3,000 people I think when I build my Tesla app even though it wasn&apos;t approved for iOS when I was waiting for like some of the numbers I was targeting that I could have like only need to sell like I think it was like 1500 subscriptions and then I would&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:06&lt;/strong&gt; · have been able to do it as my full-time like only doing that one application but then I had like 12 other applications so that&apos;s like you know my approach has always been like having multiple apps that you&apos;re working on so that they all can help each other so like you one will inspire the other one and then one will also help lift the other one because some apps are paid up front some are ad summer subscription some are free and they all like kind of promote each other in our complementary so some will have dry seasons and you know yeah it&apos;s just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;49:37&lt;/strong&gt; · a approach I take so yeah we&apos;ll talk about footer as a service by developers for developers so yeah like as Frederick said there&apos;s over two million flower developers it&apos;s an exponential growth and these are active flutter developers a lot are out in China a lot of them are here in the US and it&apos;s rapidly growing and cool too about this is there&apos;s a lot of new developers and you may be wondering like isn&apos;t that a problem but it actually is a pretty nice thing in&lt;/p&gt;
&lt;h3&gt;2M+ Active Flutter Developers&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;50:08&lt;/strong&gt; · disguise because what&apos;s cool about there the great turnover or not this is a turnover but there&apos;s a lot of new developers every year is new developers and developers in general are lazy so like you know when you&apos;re first starting you have the least amount of knowledge and when you&apos;re an expert you have the most amount amount of knowledge and the least like you know wanting to actually do it so you you have these two groups of people that you can if you have a solution that they can buy a solution&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:35&lt;/strong&gt; · that&apos;s off the shelf and plug-and-play you can actually offer this to them and then they can take that on and your that abstraction layer which is really cool because people are willing to pay for things that save time or things that they may not have to do yet this could be as something as simple as on the you know like something like unity for example the marketplace like you as a VR&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;50:59&lt;/strong&gt; · AR developer may be really good at writing the code and doing a bunch of the other stuff but you may be like super slow at building models but so something that I like to do is like you know I don&apos;t have to spend you know ten hours learning how to build leaves on a tree and how did like shade bark and do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:16&lt;/strong&gt; · all this stuff and then I have to think about all the other bazillion assets I have to add into the scene you you take that abstraction be like okay well I&apos;m going to buy this bottle I&apos;m going to just implement and that you could take the same approach for applications where you can build you know forms for business applications where they can be used in a nice way you can have layers&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;51:36&lt;/strong&gt; · of authentication protocols that they can just plug and say I want to offer Google Apple and Twitter and then kind of similar to like the firebase off you life and vice familiar but you can you provide one level abstraction higher so let&apos;s go ahead and just jump right in so first of all the market is quite large there is whoops there we go the market is quite large you have a bunch of cross-platform devs looking to save time multi-platform is a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:08&lt;/strong&gt; · you know just say sorry let me hide this real quick you know you multi flat you have multi platform issues because you have you have to deal with you know iOS Android Windows Linux web and all these things that are all happening at the same time if you have a bug on one you&apos;re gonna have to have to like figure out how to solve which platforms are facing what like you have Play services you have iOS SDK is you have web SDKs&lt;/p&gt;
&lt;h3&gt;Current Market Cross-platform devs looking to save time&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;52:38&lt;/strong&gt; · and you have to you have to write all that code it&apos;s a lot platform code but it&apos;s a great place to step in as an independent developer there&apos;s things like web deployment you know when you when you have built your application like how do you release it into the wild and this is a great place where a lot of tools especially code&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;52:55&lt;/strong&gt; · magic is a great tool that&apos;s popped up and you know been targeting just flutter and be able to and many other platforms for that matter but you know be able to say that okay we&apos;ll we have web and desktop and mobile and so you can just use this and then we&apos;ll take care of all the hard stuff for you you know so a lot of developers like don&apos;t have Apple devices and this is a great place where you can step in and be like okay we&apos;ll handle the iOS signing but this goes so much further so there&apos;s things like sparkle it&apos;s open source framework that allows you to release your applications&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:22&lt;/strong&gt; · on the open web but then you have auto updating features to where you can get notified when it update is available look and install in the background this is a framework that&apos;s used by vs code and slack and any other tool that says like an update is available with that like very generic message that&apos;s actually using the sparkle framework which is really cool and then you also got things like application application assets so just as before with unity flutter has many assets that can be used you have app icons you have screenshots you have landing screens you have so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;53:56&lt;/strong&gt; · many other host of like widgets that go with the application that go so much more like beyond like just the application itself so you have things like deeming we have material theming and flutter and Cupertino but you could build like that your own custom theme libraries you know you really target your your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:18&lt;/strong&gt; · specific product your brand and like you can do something as simple as like on WordPress does this we can have a wordpress theme and then you install it and then all of a sudden you have your own look and feel that makes it feel personal and of course you can do this from trail design but you can also do this with just generic widgets as well and I think this is a really cool place where people can step in if you are a UI developer and you&apos;re great at designs but you&apos;re really suck at like releasing applications this is a great place for use happen because you can build all the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;54:46&lt;/strong&gt; · UI component libraries you want and not have to worry about the logic and then you know the same thing goes for if you&apos;re a person that just likes doing backends and oh just love backends and I hate UI there&apos;s always a market for you too because you can be building these SDKs and command-line tools you can build scaffolding libraries and you can build there&apos;s so much that goes with the application process that you can step in and have a market for and as with any&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:12&lt;/strong&gt; · does you know you&apos;re gonna be juggling all these different platforms at once so it&apos;s like you have to choose what it&apos;s important to you and how you can step in so what are the current market needs well it is niche I display at this time&lt;/p&gt;
&lt;h3&gt;Market Needs Serving the niche&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;55:28&lt;/strong&gt; · but I don&apos;t know if you can call 2 million developers a niche it&apos;s a niche when compared to you know probably the total developer market but it&apos;s rapidly growing and it&apos;s I know flutters already past react native which is huge but there there is a market right now and you know we have things like developer tools anything that&apos;s relating to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55:48&lt;/strong&gt; · complementing the application process you have things such as CI CD platforms such as code magic you have templating engines which is like which needs to exist but there&apos;s a couple different ones that have kind of popped up I don&apos;t think any of them really stuck or like have gotten super popular training resources or things like if you wanted to create a udemy course or YouTube or video training series this is a great place if you&apos;re great at training people&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:14&lt;/strong&gt; · on how to do stuff this is a wonderful place where you can step in as a developer to be able to train the new developers coming in and you could charge for it I mean you can charge anywhere from you know ten bucks all the way up to you know hundred dollars or more for a course and you make that money every time that a new developer comes on so I think this is a great place for content anywhere anything&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;56:36&lt;/strong&gt; · relating to dynamic content so if you have a back-end in front-end sdk where you can you know manage like showing new updated information something like a dynamically generated you know widget that is running from a back-end service you know just something simple where someone has a need they want to be able to update their app without having to send a new update to the app store you can fulfill that need and then things like I mentioned earlier premium widgets so this goes for anything like paid&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:06&lt;/strong&gt; · charting libraries or paid templates that you know have built-in login screens and signup screens or something that I really like doing is like startup templates so you can say okay what is the minimal Viable flauta project that gets any login flow a you know going to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:24&lt;/strong&gt; · the home screen and having a settings pop-up that I can change the dark mode and it seems so simple but they&apos;re like there&apos;s such a big need for it and there&apos;s not a lot of people that are making it right now so the more that you can build that and then release it and sell it this will help people to and of course there&apos;s always going to be free content like don&apos;t get me wrong free is very important but you can also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;57:47&lt;/strong&gt; · make the distinction between having free content and paid content and be able to upsell on things that target businesses so whereas you know the developers that you&apos;re working with you know may not always be your market you can have it open sourced or whatever but you also want to add like premium templates so like a team like you know if slack all of a sudden decided tomorrow they wanted to use flutter then you would have to you would want to be able to have a solution that said like okay here&apos;s our&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:11&lt;/strong&gt; · different widgets and then you can buy them and you know it&apos;s this kind of a transactional marketplace that you you don&apos;t have to worry about who&apos;s using them you know very permissible licensing and you know you get the idea and the biggest thing here is to your developer helping other developers because at the end you guys both succeed there&apos;s gonna be things that you&apos;re developing that other developers need whereas like you know Fredrik for example may have like a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;58:38&lt;/strong&gt; · supporting library like wire - that really helps my applications but I could have something that could really help his applications and because we&apos;re not having to both build the same things we both can focus on our own things and then also help each other in the end so I wanted to go over some like example applications of things that I&apos;ve done tools that have approached and some kind of successes that I&apos;ve experienced so far so flutter storyboard was one of my very first developer tools I built for developers like from the day one I wanted to I I started with iOS so I was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:10&lt;/strong&gt; · very much you know using storyboard to lay out all my native iOS applications I was really pleased with it and so when I came to flutter that was one of the biggest things I missed so I released the MVP version of storyboard and it is far from finished I have tons of cool updates coming out but it allows you to with just simple wrapping your flutter widget your main app with storyboard it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;59:35&lt;/strong&gt; · will actually take all of your screens and your routes and lay them out on a canvas that you can see and update all at once so if you wanted to test your login screen beside your home screen beside your detail screen beside your setting screen you can and you can just just like you would hot reload and then all of a sudden you have you can update the change and see it live on all your screen so you don&apos;t have to navigate to the app what&apos;s really cool about this is when it&apos;s combined with flutter desktop you get some really cool stuff because I allow&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:06&lt;/strong&gt; · you to choose a device frame on the storyboard so you can say oh you can make it like a mock-up like an iPhone or Android iPad and it&apos;ll layout everything for you but what&apos;s neat about that is you can have your desktop resize the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:21&lt;/strong&gt; · window however you want zoom out the canvas to how it fits but then you&apos;re developing you know in your IDE and you don&apos;t have to have like be running on a device and flutter desktop is way faster for hot reload and of course there&apos;s things are not going to work you know if there&apos;s an SDK that&apos;s not available on desktop and it is on iOS you&apos;re still gonna have to test on a real device but I think it really gets you really far you also have the ability to yeah set&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00:45&lt;/strong&gt; · dummy data as well as you know just having like various things like screenshots which I know people are using it with and for those that haven&apos;t seen it before this is a storyboard it allows you to have all the frames and one screen really smooth it&apos;s really high performance you could have like a hundred screens on or more and it doesn&apos;t affect it&apos;s only rendering what&apos;s on the actual visible window at any given time you can fully interact&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:11&lt;/strong&gt; · with every single screen that&apos;s shown and yeah like I said this is the MVP there&apos;s a lot of really cool updates planned and could including you know panning and zooming and a bunch of other stuff with multi-touch gestures on the iPad yeah it&apos;s just a simple you know is an idea probably had and just like a weekend and really wanted to implement so next I want to talk about widget studio and if you haven&apos;t seen already I release widget studio about a week and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:41&lt;/strong&gt; · a half ago and it is actually been trending on the iOS Mac OS and iPad OS App Store in the top 25 to the upper tools what&apos;s really cool about this application is it allows you to you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01:57&lt;/strong&gt; · develop these multiple widgets all at once but in a standalone kind of a way you you know whereas you know with a flutter editor you&apos;re you know with yes code or something you&apos;re building an application and you&apos;re building a material app with multiple screens and then I wanted to create that abstraction of okay I want to create a login screen I want to create a home screen I want to create a material app and then everything&apos;s a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:21&lt;/strong&gt; · widget so then you can copy the individual widget out and then use it wherever you want or you can reference multiple widgets like on the last update which I&apos;ll show after our presentation you can actually reference other widgets on the canvas and it exports everything for you all at once and from day one I built this to export code that you can use in your application today and I know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02:46&lt;/strong&gt; · Adobe XD and some other tools have come out but they are exporting code that&apos;s really not meant to be used it&apos;s like okay using a bunch of transforms and burnt a bunch of like you know verbose kind of things whereas mine is you know doing it the fluttery way so like you know when you have a scaffold in that bar and a floating action button and a center and a text it&apos;ll do it just like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:09&lt;/strong&gt; · you would if you were building a flutter application yourself writing code the nice advantage of that is because I&apos;m building it for developers and I use it myself if any time you used property is like in the constructor it&apos;ll automatically Adam as debug outputs you automatically get it to JSON you know constructor method in your widget and there&apos;s a bunch of other stuff that I&apos;ve added which is like you know a lot of sugar but what&apos;s really cool is you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:37&lt;/strong&gt; · then have the advantage of having a color picker or playing around with widgets I love developing these widgets on like an iPad because I could be on an airplane and you know be laying out these different widgets and really start to like get to know every single widget because it exposes you every single&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:03:56&lt;/strong&gt; · property that the widget has at one time I&apos;ve learned so much about the flutter widgets just unlike the past like two weeks even after been using flutter for over two and a half years so for me that&apos;s like wow like I didn&apos;t know that textile had this one property and because of that because it&apos;s exposed out in front and center you can really play around with it and not have to worry about it messing up your project because it&apos;s isolated and it has full undo/redo so then you can just remove anything that you mess up and and of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:23&lt;/strong&gt; · course that has drag-and-drop and stuff like that but it&apos;s just an example of me building a tool that is made for developers that can be used in a real world way and another thing to kind of preface this with too is you know there&apos;s many tools like this that need to be made for flutter you know there&apos;s you know anything that&apos;s relating to networking or things that you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04:48&lt;/strong&gt; · relate to templates just like I brought up earlier these all these kind of tools need to exist because for developers wanting to save time you can help step in and be like okay I can provide this and then people will use it and not everybody&apos;s gonna use it that&apos;s not the goal you&apos;d like it like we said earlier you only need like zero point zero zero to five percent and because of that you will be very happy with you know the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:14&lt;/strong&gt; · kind of adoption you get it&apos;s not about making thousands of people happy millions of people happy it&apos;s just like worrying about maybe a thousand maybe a couple thousand and that&apos;s a whole lot easier to manage when you approach it from that perspective choose your pricing options to where you have a market that&apos;s at a price point that makes sense for you and what you can actually manage and you know things that you want to get up for and be able to do so here&apos;s a quick demo of the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05:40&lt;/strong&gt; · widget studio where I&apos;m just taking these different elements and I&apos;m dragging them down the canvas and what&apos;s cool is like it it&apos;s very flutter ease so like you know if you have a preferred size widget like an app bar you can&apos;t drop it on to a container like a container child or anything like that you have a full widget tree that you can inspect and edit every single property that the widget has you can then use even since just making this gift I&apos;ve added like 20 more widgets so we&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:13&lt;/strong&gt; · adding ton of widgets every single day and we&apos;re targeting the end of the material catalog very soon but you know it&apos;s just you know it&apos;s just a simple tool that allows you to that solves the need of you know exporting for flutter so as you see here I do two brackets and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:29&lt;/strong&gt; · then I say count something very simple but when you go to the export code it&apos;ll automatically give you a constructor option that says count so then when you&apos;re using this widget from other places you have that option that you can then send on so it&apos;s kind of like a almost like a view model in that aspect so there&apos;s also things in regards to application support so I&apos;m working on some applications that help you design icons for your your iPad Mac OS and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:06:59&lt;/strong&gt; · Android applications you know easy like be able to upload and download an icon resize it things on you know that also should exist and that I&apos;ve kind of taken stabs up but if kind of like in the open source level right now but PWA manifest generator privacy policy generator these are two things that like are needed in every application and photo right now but there&apos;s not like a good you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:23&lt;/strong&gt; · tool that&apos;s you know there&apos;s tools that generate it but like being like the owner of that and being like ok I&apos;m gonna do this better than anyone and we do it right and I&apos;m just gonna nail it to where it&apos;s like people like oh yes like I&apos;m gonna go to that tool every time that&apos;s like where you want to be and that&apos;s how you serve the niche and just as a side note just because there&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:42&lt;/strong&gt; · many apps that exist in a market that doesn&apos;t mean that you can&apos;t enter the market if nobody is really serving it well that&apos;s probably the reason why there&apos;s so many competitors or so many tools available for example when not one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07:57&lt;/strong&gt; · of my very first photo applications which I originally built native was a pitch pipe and when I originally built it there was plenty of pitch pipes on the app store I mean there was at least just in the one category alone there was probably over 100 but I decided to take&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:12&lt;/strong&gt; · an approach on the pitch pipe that was completely different and made it super clean professional like you know very modern and because of that like mine like reached like it&apos;s now the number one and has been the number one for over a year and much longer than a year but in the last year like nobody&apos;s come close but the purpose of that is because all the other people have just built it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:34&lt;/strong&gt; · in a way that didn&apos;t solve what people are actually warning and so when you find and you serve that niche you really can help developers and anyone for that matter really take a problem solve it and then they&apos;ll be your brand champion so other things to real quick you can also build screenshot editors so you know every App Store Google Play and Mac OS Windows Linux all need screenshots&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08:58&lt;/strong&gt; · and it&apos;s something that see really simple but can be a pain in the butt and what&apos;s really cool about flutter and the ability to override your target platform you can actually generate the screenshots without running on an emulator as long as you run it in the specific dimensions you can they&apos;re valid to be exported and then other simple things like a splash screen editor because this is something that&apos;s often forgot about but I know on lower-end devices this could be a real pain so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:26&lt;/strong&gt; · here&apos;s just a quick example of me building an icon generator it allows you to you know upload any icon you want and what&apos;s cool is when you download it downloads an archive that you can literally drop directly into your applications so it creates all the folders for you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09:43&lt;/strong&gt; · in here I&apos;m going to show the individual ones but what&apos;s cool is like you have the icons in the correct format in the correct folder structure and you literally it&apos;s a plug-and-play kind of system and then you as a developer just worry about creating your your one icon what&apos;s cool on the new WBC announced that we can now support SVG icons which is going really great for iOS and Mac OS but still paying the button on web and and Android at the moment and Windows for that matter so some other things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:13&lt;/strong&gt; · that I&apos;ve been doing that also need tons more tools built with is CLI tools not every developer love CLI in fact a lot of people that like no code tools H CLI or anybody that&apos;s like a designer usually doesn&apos;t want to do CLI but for developers and back-end developers this is like bread and butter like I&apos;ll choose a CLI any day because I can build&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:37&lt;/strong&gt; · my own tool around it so things like app scaffolding templates app clones just really easily be able to say like you know whatever generate this and then it just generates it to where you can have it exactly how you need always in the way you need it this can also be just as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10:54&lt;/strong&gt; · simple as like no angular has this generate system where you can say ìokay generate this component generate this service generate this and once you introduce structure you can go so much faster every single one of my applications follows the same folder structure the same pretty close to the same state management but I have the same structure in every single one of them and because of that I can copy and paste between projects I can jump into any project no matter if it&apos;s like a year ago and know where everything is I&apos;m never confused and it&apos;s because you have that structure and it&apos;s ok if the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:24&lt;/strong&gt; · structure of vols but you got to evolve with it so it&apos;s like you know if you do a new practice you got to make sure to implement it in all the places to where it&apos;s consistent when you don&apos;t have consistency that&apos;s when you&apos;re going to really bite yourself later so other things like reducing boilerplate so anytime you have the successive amount of stuff to do a quick example is I took&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11:43&lt;/strong&gt; · I made easy web view which is all the boilerplate for regular web view in just simple tools like that where you&apos;re handling that abstraction layer of doing all this complicated unnecessary stuff but then the developer just gets to have like a cup align implementation that is prefaced with the fact that if they want total control they&apos;re not going to be using anything if a person needs all the fancy&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:06&lt;/strong&gt; · callbacks and you know whatever they want they&apos;re gonna have to do the regular implementation just as what you&apos;re trying to do your goal where you&apos;re stepping in is for the lazy person that doesn&apos;t want to do all that stuff and just says like I want it like in five minutes be able to build this application and I just need a webview or whatever insert widget here other thing too is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:27&lt;/strong&gt; · code generation I think Remy has done a wonderful job with freezed and a bunch of his other packages that relate to code generation where you can say okay here&apos;s a specific need Dart where we needed immutability and json serialization and copy with constructors and stuff like that and you can be like okay well i can build a package and you know a set of tools that will do all this for you another area where you can do is localization I know there&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12:55&lt;/strong&gt; · localized e and some other ones have targeted flutter but it allows you to say okay well we have this this problem which is internationalization and flutter applications and we have a solution which is building your your JSON files of your or your AAB files for all your translation files like I said earlier dynamic content and also just a service abstraction of insert whatever service you&apos;re using you can then be the abstraction for the person that&apos;s using it so um another big thing is you got to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:25&lt;/strong&gt; · find your market because not every you know market is the best one and a lot of it takes like I know Frederic talked about it a little bit but you know you gotta see what the markets gonna be first you gotta put something out there let it bite and see okay actually that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:41&lt;/strong&gt; · way different than what I was expecting or oh wow people really love this and that&apos;s the thing I was carrying the least about so it&apos;s like you know it&apos;s a it&apos;s about tailoring your your applications over time and being flexible to say okay well the markets kind of taking this shift I&apos;m gonna see where it takes me and one of the things I&apos;ve learned in my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13:59&lt;/strong&gt; · life too is being ready for when that wave happens like you don&apos;t need to go buy a surfboard when the wave has already started you want to have the surfboard and be ready way before the wave is you know come to you because one you&apos;re there and ready you get to you get to experience all the joys of being the first mover and having the great&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:18&lt;/strong&gt; · market of you know being with others but if you&apos;re always playing catch-up like you&apos;re never going to get ahead or it&apos;ll be really really difficult you want to find people that are highly motivated and willing to pay and funny enough developers are one of the people that love and know what it&apos;s worth to pay for applications not every developer but I would say most developers understand that like if an application is $10 or $5&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14:45&lt;/strong&gt; · or even $1 like a developer with would probably not think twice about buying something whereas a person that is not a developer would be like oh I&apos;ll never pay over XML and in fact I buy so many applications just because I want to support the developers regardless if I use them myself we also have you know like I said you want to make sure you evolve over time and then support the new developers coming in this is kind of a unique field where flutter is for better for worse&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:16&lt;/strong&gt; · targeting cross-platform development in a both complex and super easy way so like you can use flutter and not know about Swift C++ Linux or you know c-sharp or whatever you need or Java even for that matter or Kotlin but and you can only use Dart and then you can build all your applications it&apos;s great but there&apos;s also the developers where as like myself if you know how to write the platform code then you&apos;re in like a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15:44&lt;/strong&gt; · really good position because you can be building these really custom tailored experiences but because flutter tailors to both you have a lot of new people coming in that&apos;s like this is the probably their first introduction into app development so they&apos;re gonna be only knowing Dart and they&apos;re gonna need some tools they&apos;re gonna step in so maybe you&apos;re just a developer that knows how to write Swift and and Kotlin just a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:06&lt;/strong&gt; · simple example well you can then be a contractor for writing plug-ins so if they say okay well we need X Y &amp;amp; Z you can step in and be like oh yeah I&apos;ll build a plugin for whatever your company is needing so you get to build plugins and then they get to just build the applications just another quick example so yeah it&apos;s really exciting stuff and it&apos;s all about being ready for when the market comes and then lastly too we want&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:31&lt;/strong&gt; · to talk about like pricing options because you want to make it worth your time you don&apos;t want to be like spending all this time developing and creating this stuff to like have like like it all fall on your face because like nobody wants to pay for it or it&apos;s free and then like or like maybe you only have like one user and you know it&apos;s like there&apos;s so many areas in which you just got to be so careful so some things that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16:53&lt;/strong&gt; · that I kind of take it approach is like there&apos;s there&apos;s a couple different models you can take which is like subscription which i think is really good of course people will have subscription fatigue but it&apos;s about offering it at a price that makes sense to you and your users paid upfront applications are one of my favorite type of applications you there are disadvantages of course as always but for me the trade-offs are really nice so first of all with paid upfront applications at least on iOS and Mac OS is I can I don&apos;t have to deal with the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:25&lt;/strong&gt; · in-app purchases which I have many applications and I&apos;ve done it for but all that abstraction is just like okay well if they want it it&apos;s gonna be like you know they&apos;re gonna see the reviews and it&apos;s up to them to whether they get it what&apos;s nice about that is apple offers search ads which is really cool where you can actually price your your application at a certain level you say I want to spend X amount on search ads and you only pay for when the application is installed so you&apos;re not having to pay for general marketing and you can never&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17:56&lt;/strong&gt; · lose money on marketing which i think is a great place for paid up front applications like I said there&apos;s other disadvantages people like Oh what about a free trial and stuff like that and this is where what I approach I&apos;ve been taking recently is offering the web version as the free version I totally believe in the open web I think web has been the best thing that has ever happened for mobile development and I love how powerful you can make&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:22&lt;/strong&gt; · things on the web flutter is a great example where you can release something on the web and all these native platforms and offer these experiences to where if they want to try and do whatever they want they could do it on the web but if they want like a higher quality experience that&apos;s a you know multi-touch gestures and doing all the like the native offline sync and the high performance rendering and 120fps all that kind of stuff happens on the native applications but you still get to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:18:48&lt;/strong&gt; · offer your product for the people that want like a PWA and to be installed on their home screen for example on the web and they don&apos;t have to be the same experience it can be when I think it&apos;s great when it is flutter uses a skia as an optional flag which makes it really good and performant and actually it&apos;s really hard to tell the difference between native and web but other things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:10&lt;/strong&gt; · too is you know you can offer courses on you know for flutter and any other frameworks another nice thing too is support so for example like if Frederick wanted to hire a team to do support he could then target you know really big businesses and be able to charge tons of money for support because businesses are willing to pay for that you don&apos;t even have to be the one supporting them and I think that&apos;s great for even other applications too because it&apos;s something&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19:39&lt;/strong&gt; · that you don&apos;t realize how much people value at big businesses just by having a 24/7 access to a support line they will pay you just paid name the price and they will pay it like they will choose your tool over other ones based on the support and then in tying in with that having teams to be able to just support ok collaboration on your tool and be able to working with other people and just like having the options for you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:05&lt;/strong&gt; · know who can pay for what so what are some future ideas that you can do as you know flutter developer general developer or someone just looking in to get into the market well like I said earlier I think app translations are another thing that I don&apos;t think it&apos;s completely served yet I think there&apos;s still room for a really&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:25&lt;/strong&gt; · good or maybe a couple people to build an app translation service that&apos;s not only easy to use it works way better with flutter something that you could like literally extract stuff and have ability to change on the back end and I know localize II has done a lot of it but I still feel like there&apos;s some more that can be done there database tooling so anything relating to like sequel Lite or the persistence options this is still something that&apos;s an open question realm is really flirting with flutter at the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20:53&lt;/strong&gt; · moment so it&apos;s a it&apos;s getting close but not quite there yet other dev tools just because flutter releases dev tools doesn&apos;t mean that you can&apos;t release your own there&apos;s been some other examples of people on Twitter that have announced different dev tools that they&apos;ve made or examples and I think that&apos;s really cool a marketplace this is still yet to be made with flutter you know a place that where people can go and and buy these resources just and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:19&lt;/strong&gt; · then I just like to say on that one people have built marketplaces but it&apos;s important to take this from a perspective of collaborating with other creators building in a way that the developers and the creators that are creating want to be on this marketplace so whatever whoever builds it it needs&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:37&lt;/strong&gt; · to be something that people want to use and it&apos;s not like it&apos;s not about like whatever the prettiest tool wins because that&apos;s not the case it&apos;s not about whatever usable tool wins its it&apos;s about whichever tool solves the need the best in a way that helps everyone because it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21:57&lt;/strong&gt; · may not be the best tool but if it if it solves the purpose really really well doesn&apos;t have a lot of issues with bugs and stuff like that that usually is the one that wins out and then like I said earlier other resources that relates to flutter applications you know whether it&apos;s like I mean something even if you&apos;re just really good at writing a book something I think would be really cool that rest is done is building a dart book you know or a flutter book and just have like a really simple like interactive like from here you know zero to hero you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:25&lt;/strong&gt; · know just say like okay here&apos;s how to do this this and this and this and you know stuff like that I think is really good so a conclusion you want to build and develop for other developers anytime you were saving time that is an opportunity cost for someone else that is going to be doing the same thing so that&apos;s where you can step in and be like okay well actually we can save a lot of money if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22:49&lt;/strong&gt; · we don&apos;t have to do this and I&apos;ll be willing to pay XML so anytime you see that and you&apos;re like wow this took a lot of effort you&apos;re like huh I wonder if I could actually just build this as a package and you know sell it for someone else or give it away but and even if you just as a side note even if you don&apos;t want to charge for anything there&apos;s other options out there you can also do like patreon and have like buy me a coffee and PayPal donation links to where if you there&apos;s plenty of people that just do that and then of course&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:17&lt;/strong&gt; · having YouTube channels and whatnot so keep that in mind too that you can still do this and still have support but I do believe that you want to make it worth your time regardless of what you&apos;re doing charge when it makes sense just like I said and then also something should remain example it&apos;s not every product that you&apos;re gonna make should become a product it&apos;s sometimes just be better just released as an example and be like okay you know this is great but like you know I think I&apos;m gonna defer to other people like for example like I built a podcast player and it&apos;s like I I&apos;m crazy&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23:50&lt;/strong&gt; · about music applications I build them all the time but you know there&apos;s so many podcast players I don&apos;t want to compete in the market so I just made it open-source and released it for others so just a simple example you know there&apos;s a growing market and new developers keep that in mind because you&apos;re gonna be targeting people that never coded this is the first thing they&apos;re coding with or people that are very experienced and don&apos;t want to do anything at all so build for both those&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:16&lt;/strong&gt; · audiences serve the underserved if you were finding people that you know are frustrated you know those are great people that would love to find this kind of service if people&apos;s every if everyone&apos;s happy then there&apos;s not really a market for you to have and then like I said earlier support is worth paying for and way more&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:24:35&lt;/strong&gt; · than you actually think yeah just look up some of like your favorite your favorite tools and just look how much like support cost for 24/7 it would be pretty surprised like we can be sometimes talking about like triple digit so yeah we&apos;re gonna jump into questions real quick but just wanted to say thank you so much for listening to this I I&apos;ve been working on this for a while I just wanted to you know talk about some of these things that I&apos;ve been going through and experiencing personally and really excited to kind of hear you guys feedback&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:06&lt;/strong&gt; · you all right so let&apos;s go into the questions yeah first of all Rory I think this was a really dense and interesting we have a few like participants here and I think you dropped so many ideas there should be at least one for everyone and I am I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:36&lt;/strong&gt; · can&apos;t emphasize I emphasize more the fact that young people should just bill something that actually fixes the problem or fixes for them and to emphasize the fact that you just need to find like something like this very small&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25:52&lt;/strong&gt; · thing what you was referring to her service or maybe underserved search is like a few well maybe it just thousand one thousand developers right and you could live up that that&apos;s that&apos;s something else and don&apos;t always approach stuff with this you know shoot for well the moon kind of mindset although this is great and I like this as well but you know don&apos;t burn your your fingers right like with those big ideas just start small try to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:20&lt;/strong&gt; · get your food into the door with a small idea that you may be charlie to flutter only and then you can evolve out of it like you just you just build a flutter event at some point maybe you see oh there&apos;s meat for it like fellow developer have this neat or maybe even multi-platform developers have this need and then you report it over to react as well and then you grow and then you become at one point like after a few years you become the market leader and this is in my opinion how you grow a sustainable business plan out of like parting with a niche and then having this organic growth and I think even the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26:52&lt;/strong&gt; · big players very like Google Facebook all of them they started actually very small and never thought that they would be multi-billion companies so yeah I think that&apos;s very very important and a powerful message as well for people no thank you so I&apos;m just gonna jump into some of the questions so I&apos;m sorry I&apos;m not gonna pronounce any of your names I don&apos;t want to get them wrong so we&apos;re gonna go with how do you manage learning new things and prioritize so for me I I&apos;m really&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:26&lt;/strong&gt; · big on creativity and passion but those inter you know mingle all the time so it&apos;s like one thing that you stay you know keep from burning out two is you stay up on different things that are unrelated to what you&apos;re doing so this means like you know doing stuff in VR and doing stuff outside like coding and music and performing and sports and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:27:49&lt;/strong&gt; · everything that&apos;s unrelated helps you with coding like if you&apos;re only doing coding you&apos;re only gonna be good at coding but if you learn these other ideas then you&apos;re gonna be like oh you know what I actually did think of that from over here and you know creativity is just innovation you know regurgitating different ideas and that you know outputting different ones I&apos;m just gonna kind of do just a quick a couple other ones real quick how many I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:15&lt;/strong&gt; · can elaborate on this yeah yeah of course yeah I think it was a quite a long question as well because it was all saying there&apos;s a lot to learn and I end up not doing much Q the procrastination overhead right I think most importantly&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28:30&lt;/strong&gt; · is not to think about doing stuff but just doing you know this is this is a very important thing you know just start with something small if you think about something like I want to build a smart home application right just start with something small start with an app that has it has a button that turns on a light right that does like some kind of memory quest and then from there you will go from there because you will get motivated on the way it don&apos;t over think this is like literally the worst thing you can do that I always do myself if you think too much about a possible solution like even our database schemers&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:02&lt;/strong&gt; · how you use factory data all that kind of stuff you know be assured you can always change the stuff later on if it gets but just get started don&apos;t let this well if you&apos;re saving and if you&apos;re saving into version control like yeah I know like people they&apos;re always scared it&apos;s like well they never saved and it&apos;s like yeah of course you can&apos;t be able to refactor you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:26&lt;/strong&gt; · sometimes I keep myself like just overthinking we all know this phenomenon where we think about name right which is just like you can rename it at any time if you come up with a better name in some point right so just don&apos;t look with a better name you&apos;re never gonna get to right the first time babies you know just continue a little work with what you have this is a very important thing otherwise you will always like over things stuff yeah and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:29:52&lt;/strong&gt; · the idea of like incremental improvement like you know it doesn&apos;t have to be like a version 1 version 2 version 3 every time like you know it can be very much like okay all I did on this update was just you know choose a new font and but or maybe I just like changed a couple things but you know like I&apos;ve more recently switched the idea just like okay you know what I&apos;m just gonna release things as I add them and stop like trying to be like this wave of pressure of just like okay well 2.0 is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:23&lt;/strong&gt; · out and it has this isn&apos;t this in this and now it&apos;s time to jump on because then all you&apos;re doing is creating people that just are waiting for the next version versus like if you&apos;re outputting all the time in which by the way we talked about this before anyone joined or as a couple were but you know this is a great idea for like a Twitter account and when you have a Twitter account for your application you&apos;re allowing you to generate hype over time and you&apos;re destroying the barrier of one version 2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:30:48&lt;/strong&gt; · version 3 version you&apos;re just saying here&apos;s some exciting updates here&apos;s some discussion about it and you get to really connect with your users on an individual level and see like what is actually they&apos;re wanting because if like my choir - is a perfect example you don&apos;t want to build something that nobody wants and you want that feedback you want to be like ok well I really love it and know what everybody else hates it you know that&apos;s not where you want to be and you will be surprised&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:19&lt;/strong&gt; · that people know that you&apos;re building this they&apos;re also like they don&apos;t care something&apos;s not working you know if they know it&apos;s basically developer you know roadie build this and I I don&apos;t expect some people rodeo that I would expect on the big corporation I would just a project only differently and you really notice that in the community as well so people like yeah you know I just promise but if you can you know check it out in the upcoming days that&apos;s fine it&apos;s not like I fold this but I don&apos;t uses em anymore it&apos;s like how dare ya&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31:51&lt;/strong&gt; · play right you stop working most of the users are like Anya you know this is the shitty app you know I just like uninstall and your gum they will never download it again but if you if you&apos;re building something for a community which you&apos;re a part of and people know that that&apos;s like way they&apos;re way more forgiving and they they like to this is very well I would like to say though like don&apos;t feel nervous like you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:17&lt;/strong&gt; · bill apprehensive but like like don&apos;t feel like you&apos;re being judged in the community by it either like just because you&apos;re creating this like this is not like a oh well Fredrik really sucks at doing this kind of X development and it&apos;s like you know it&apos;s like you know it&apos;s it&apos;s a your product can live outside of yourself as well like you know like your product can be bad and you can still be a good developer you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32:40&lt;/strong&gt; · I&apos;ve made plenty of bad products it&apos;s like you know it&apos;s like they don&apos;t define you and I would just say like because when I was first starting out with coding and development that was my problem is I took everything way too personally I&apos;d be like oh they hate this like oh this is they just hate my design and so it&apos;s like you know you second-guess everything but yeah I think it&apos;s really important to remember that there is that distinction so when you&apos;re doing this you know you know develop it for the community and for yourself first because if you want to be using it all the times and there&apos;s a chance that the other developers in the same technology do too so I&apos;m gonna also talk about so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33:16&lt;/strong&gt; · the free trial i talked about my talk but yes you can offer free trials for paid applications you can do this on both Apple and Google Play however I like to have the web version as the free trial because it&apos;s an unlimited free trial they can do it as long as they want and whenever they&apos;re ready to upsell they can just install it my kiss transactions you do it good for interrupting I think there&apos;s actually a really interesting question as well that you might have missed this yeah I think&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33:50&lt;/strong&gt; · it&apos;s actually interesting question so maybe you have yeah yeah I think it&apos;s a huge domain right now currently there&apos;s a couple different players in the space there is a charting library I cannot think of their name at the moment but they they have like oh is it called sink fusion yeah sink fusion charts they offer paid flutter widgets for these really complex like charting kind of stuff I think this is a great place a package that I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:15&lt;/strong&gt; · working on that is released but like far from like the level of where I want it to be is called data tables there is a great data tables library that exists for the web and there&apos;s nothing like that for footer yet where is like you&apos;re taking like a material design implementation of a data table and you&apos;re adding all the other features like download is Excel searching and reget reject search and be able to have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:37&lt;/strong&gt; · column sorting and all the kind stuff be able to step in and be like okay I&apos;m going to provide that best experience you just give me the JSON data or c.coli data and I can handle it from there so that&apos;s another great place premium widgets also a great for anything relating to firebase or AWS or you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34:53&lt;/strong&gt; · insert your you know whatever cloud back in here you know you get to provide widgets of like okay here&apos;s a login button the login button does all the abstraction layer for doing you know showing up the pop-up screen showing up this and then all you have to do is a flutter developer as you customize what you say on the button making sure you have the correct files installed and it does everything for you something super&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:35:15&lt;/strong&gt; · simple you know if you&apos;re I&apos;m a huge fan of block specifically flutter block and some other state management tools but I built block libraries that allow you to just have a drop-in solution for authentication to where it handles all the logins sign out sign up change email change you know whatever you as a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:35:32&lt;/strong&gt; · developer just make sure you call these specific methods I think this is a great place for premium widgets and it&apos;s a huge market and not everybody&apos;s gonna pay for them if you don&apos;t want to pay for something you&apos;re more than welcome to build it yourself keep in mind the you know the theme of this talk is you are trading off the opportunity cost if you&apos;re saving time that&apos;s where someone is willing to pay not everybody is willing to do that they&apos;re willing to put in the time to build it themselves that&apos;s okay you are targeting the people that just are wanting that convenience&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:36:07&lt;/strong&gt; · I want to also related to that I only talk about the micro transactions and the unlock features with in-app purchase and yeah I have an application that&apos;s completely built around you know and I purchases for unlocking stuff and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:36:26&lt;/strong&gt; · subscriptions and stuff like that that&apos;s another place I think the tool needs to step in revenue cat exists but it&apos;s currently a pain in the butt on both on clutter to be able to like both test iOS and Android there iOS is announcing some new SDK is there gonna make it a lot easier but Android and iOS at the moment&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:36:44&lt;/strong&gt; · are really hard to test to see like you know making sure the payments went through testing the devices and what it counts but yeah and then yeah the development online compiler I&apos;m online compiler I&apos;ve actually made a ton of progress I&apos;m actually did it rebuilt it in aqueduct which is really cool so you can build run and update any file remotely which is really cool&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37:11&lt;/strong&gt; · do you see any other questions who made target Fredrik you can just call him out if you want me to ask tomorrow maybe yeah what is your current opinion about&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37:33&lt;/strong&gt; · yeah yeah yeah I can talk about it so rephrase that it all depends on the application itself I think widget studio and some of my other applications are great used for flutter web because it&apos;s like very like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37:50&lt;/strong&gt; · it&apos;s an editor you know it&apos;s a bunch of like components and things you know all clicking and you have the state management and there&apos;s so many like little nuances that like I think flutter does great because you&apos;re not how to do CSS you&apos;re not having to do anything you&apos;re just literally paying to a canvas in handling at all things that aren&apos;t&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:38:07&lt;/strong&gt; · great for flutter web and I built my website and flutter web and some other things and recently change them back to Jekyll because it&apos;s not great for start static content or server-side rendering content right now so anytime you have like a post or things that are just like you know dumb websites that have no application state and they&apos;re just literally HTML files yeah flutter web is not the solution it doesn&apos;t mean it won&apos;t be the solution in the future but right now I think anything that&apos;s static&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:38:35&lt;/strong&gt; · should probably stay static and honestly hTML is not bad it&apos;s gotten a lot better and with web components and a bunch of other things that have been added recently to the web it makes it pretty easy to do that so yeah yeah for me I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39:06&lt;/strong&gt; · a huge fan of firebase and GCP back in so I make sure to have Google Analytics and all my applications or most applications I track events for a different kind of stuff I can really easily measure like kind of a general Apps vibe on like okay people are using&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39:22&lt;/strong&gt; · this screen way more than I thought or just simple things of like if Anapolis crashing or not I kind of get like a barometer of all my applications to see which ones are you know good what&apos;s the general feedback and I also measure my analytics on Google Play and Appstore for seeing like okay it is like a trending upwards downwards as a scale is nobody buying it so based on that it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39:45&lt;/strong&gt; · really kind of dictates when I do updates for apps I try to only do an update for an app when it comes down off the bell curve just a little bit because there&apos;s somewhat of like a and this is not for new apps this is for like existing apps but when your app is starting to go down that&apos;s a great place to step up because otherwise few too many updates in a row like you kind of like are competing with your own updates so it&apos;s like you know you may make an update and then like someone like only sees the latest update and doesn&apos;t realize that these X features were in there because you just overshadowed it so giving space to have the updates has&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:40:17&lt;/strong&gt; · really helped me so it&apos;s like you know I may only have to update like an ass like three times a year but as long as there&apos;s a really solid updates usually that can be really useful and then it&apos;s also about managing you know like for maybe testing for example I love to use the web for my my beta play&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:40:34&lt;/strong&gt; · so it&apos;s like I&apos;ll just change do crazy you eyes and do whatever and then I consider my iOS applications and my Android applications as the stable branch so it&apos;s like it it&apos;ll it&apos;ll update way slower but it&apos;ll have the refined almost no bugs you know stuff like that so I think that&apos;s a really cool distinction to like be crazy on the web be stable on the App Store&apos;s and then you get people that are really solid and want the applications and and the support and the you know just like I never want this to fail but then you also get the people that want to try out all the crazy crazy new features to do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:41:05&lt;/strong&gt; · you have one or two like favorite kind of side projects you&apos;re working on which you give priority to probably maybe it&apos;s like yeah so for me right now I have two projects that are taking the most of my time which is widget studio which is still been trending and top 25 of the App Store so that takes a lot of my attention right now and then also hymns for worship as another app I&apos;ve built which is cool because it&apos;s a globally distributed sheet music and presentation creator so it&apos;s used by over 3,000&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:41:36&lt;/strong&gt; · people like almost every day so it&apos;s like very popular and because of that you know there&apos;s a lot of issues that come when you when you have a popular application so support and bugs and things aren&apos;t working so yeah those are the two that probably take the most of my time of the 12 apps that&apos;s awesome I&apos;m just skipping through the questions yeah you mentioned solution what is your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42:09&lt;/strong&gt; · view on using Dart generally on the backhand side yeah so it&apos;s always my first solution but there are solutions that I think it&apos;s great if honestly if you&apos;re using something like protobufs or anything that&apos;s related to G RPC it doesn&apos;t matter what you&apos;re using on the back end but with that all being said I built backends using C sharp net I built backends using you know just straight-up you know general nodejs or something as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42:33&lt;/strong&gt; · simple you know I&apos;ve also played around with rust and all these kind of things I think dart is really cool especially with the asterisks of like you know dart compiling to native like dark to native because it allows you to really just submit a by and run it wherever you want I think dart is really great for that a cool thing about aqueducts and the reason I chose it is it allows you to run on multiple isolates so you can have a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42:58&lt;/strong&gt; · basically however many cores your computer has that&apos;s how many cores the the HTTP server can run on so I think that&apos;s really cool do you think it&apos;s ready for primetime like would you buy it yeah they&apos;re big scale production yeah I definitely would okay&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:43:27&lt;/strong&gt; · yeah that&apos;s my current weakness I like documentation slows me down and I know I have to do it and I know it&apos;s important but yeah that&apos;s something that yeah that&apos;s something I struggle with is having the time and writing out the documentation because for me I feel like the second I write the documentation I&apos;m stuck to it because it might might have changed so much but it may be also a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:43:50&lt;/strong&gt; · good thing as well to kind of have some stability but yeah I really like so if you&apos;re building an HTTP server I really like auto generate documentation so things like open API is really cool I know I think it&apos;s called um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44:07&lt;/strong&gt; · not swashbuckle but uh there&apos;s a there&apos;s some kind of framework where you can use this with opening api standard it&apos;ll generate your api documentation the same goes for flutter applications you can actually generate and annotate your classes to where can actually swagger thank you and you can generate your your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44:25&lt;/strong&gt; · documentation using dart doc so it can actually you know give you stuff like that but i kind of like to take a different approach to like making youtube videos and blog posts and as well as i like to have it dynamic so things like you know in-app workflows to where it&apos;s targeting specific widget keys that way no matter if i refactor the app it still you know is always up to date because like ii you take a screenshot it&apos;s already outdated you know that&apos;s that&apos;s always a pain yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44:54&lt;/strong&gt; · because it&apos;s very much of a dynamic thing cool yeah maybe I can also add one point to that I for why - I&apos;m currently using issue through press so it&apos;s basically all that exciting writer which is kind of good I mean it&apos;s it&apos;s free and open source you can also go with something like get booked but then it&apos;s already paid from the first second on yeah thing about if you go with something like blue press is that you can just have you read me files right and then maybe basically deploy on on a committee&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45:23&lt;/strong&gt; · or something to notify it just you know push your documentation life and basically also running for free if you&apos;re like having small product so or small documentation so that&apos;s yeah yeah I think another interesting question which I think you more or less already answered it is how would you approach&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45:44&lt;/strong&gt; · server-side rendering on full web for CEO purposes right like I mean I think that&apos;s a very important question especially for like federal replacing web development right because obviously you want search engines to index your site especially if you&apos;re building like a meta search engine or whatever or something where the condom is very important to be in that frame so do you think there will be a solution in the future or what are the current views from your on this hoping and regatta&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46:17&lt;/strong&gt; · flow yeah yeah so there&apos;s a couple different ways I can answer that I will answer it with the first of all the floater team is working on solutions to make your websites more SEO in fact since flutter web is launched it is way better than it was things like generating a virtual semantic tree things like generating virtual site maps and stuff like that so those do exist PWA manifest is really&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46:43&lt;/strong&gt; · helpful for your application you know there&apos;s a lot of things that go around that taking one step further you know optimizing for SEO amp store was a footer application on the web we built that needs to be very SEO dependent so things like your URL routing is really&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46:59&lt;/strong&gt; · important you can actually Simon has done a really good PR to the flutter thing that&apos;s not quite merged yet but it removes the hash so there&apos;s things like just disabling the hash can fix most the issues there but just having URLs that are you know static and changed and have the same content as long as you keep using them Google will pick them up and then start to serve them as well this also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47:23&lt;/strong&gt; · you know taking it further things that I&apos;ve done in the past to kind of get around some of the limitations is using firebase redirects I can actually redirect to firebase functions that then return static content so then like let&apos;s say I have a blog post for example I can link to that URL and it returns the markdown in HTML form or whatever I want&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47:45&lt;/strong&gt; · and that&apos;s indexable on the web that&apos;s copyable and whatever but you also have a flutter web application so you can build these like kind of a hybrid approach which I think honestly is going to be the first approach yeah I think that&apos;s the biggest thing it&apos;s just like you making these trade-offs but that being said nowadays it&apos;s like there&apos;s a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:48:09&lt;/strong&gt; · lot of people that care about SEO I&apos;m one of them but it&apos;s also apparently not as a big deal as a lot of people are making it out to be like if you&apos;re a small business SEO is really important but if you&apos;re an application your your URL is the important thing so if you can be getting people to go to whatever app that&apos;s way more important than making sure your your blog post three pages down it&apos;s always linked yeah I also totally agree with that it depends on what you use is for for the kind of app&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:48:39&lt;/strong&gt; · right if it&apos;s like really like an app that you would run in your browser or you would want to rap yeah then I think it totally makes sense to think about using clutter but if you still want to do something like which you can involves also a lot of text and then all stuff like that then it needs to be indexed I I don&apos;t think that it&apos;s quite ready yet yeah yeah I think yeah so I think&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:49:04&lt;/strong&gt; · there&apos;s just like some kind of questions even rolling in so there&apos;s for internet what is your approach to integrate for UI into a big existing code base rewrite from scratch and arm or try to reuse the Oh Jake intrepid and a son of us my talents yes one of the fruits one of the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:49:24&lt;/strong&gt; · first applications I built was a native application on Android and iOS and the first thing I did was rebuilt the flutter UI and communicate through platform channels it works great it has a binary messenger you can do a lot what&apos;s even cool is that confusing Kotlin on Android you can actually use Kotlin native and then call into it with FFI so you can actually take your existing Kotlin code deployed as a module in a library and then import that inside of flutter and then just call into it just like that but for me honestly I don&apos;t know if a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:49:55&lt;/strong&gt; · lot of people know this but if you D compile it into Java you can literally copy most dart objects directly from whatever and convert it to Kotlin and that&apos;s really close to dart so you can yeah there&apos;s some really cool things there where you can you can actually migrate your cloak code really fast and some things that you don&apos;t think about when building on Android is lot of things that&apos;s like very verbose in Java or Kotlin or maybe not calling as much but you can do it and just like half as many lines in dart and then also like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:50:25&lt;/strong&gt; · things are just way easier I think in a lot of cases like with networking you just do HTTP GET and then like it does everything versus like you know having to set up some different stuff but yeah I would say it&apos;s up to you depends on if you&apos;re on a huge team if you&apos;re in a big team then obviously add app is the solution and then you&apos;re gonna migrate slowly otherwise if it&apos;s just yourself then you could probably knock it out in a week I also I also would add that it really depends on how big your code base&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:50:54&lt;/strong&gt; · is and what kind of you&apos;re dealing with I can just add from not from like my experience but maybe you know Pascal Pascal she&apos;s also like a flutter Gigi and has been working on like some agency stuff recently and converting exactly existing Android apps into clutter or like bringing iOS and Android apps into the fodder kind of ecosystem and you say&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:51:17&lt;/strong&gt; · like for most of the time this experience is wave types of actually rewriting at the flutter then dealing with you know at web and all this stuff that you know comes with it so most of times since flutter also reduces your code too let&apos;s say around 20% or something of what you have in Android and iOS it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:51:39&lt;/strong&gt; · most of the time it&apos;s faster to just like it started raw and just try using that and then maybe use the functionality that means hardware components and create a channel for that I think this is still the way to go to be honest yeah yeah what is your approach for project management especially having multiple projects I started using Trello yeah I think yeah you know I still like to use fellow but roadie maybe you want to add something to that what do you use how do you organize your projects so the way I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:52:15&lt;/strong&gt; · archive my brain is I use bear it&apos;s a really cool application that allows you to have one too many notes it&apos;s the meaning that you can have a note that it can exist in multiple folders just by using tags I think it&apos;s really great I store all of my information there for my ideas by I scratch off terrible ideas my things I don&apos;t want to forget and so from there then I generate like MVPs and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:52:39&lt;/strong&gt; · examples which I will do github I like to keep all my project boards I use Trello for some stuff but I actually use the repos themselves to have the individual project boards and I have like one global when I get up so that that way like it&apos;s very scoped not gonna be okay this project this is this state at this time and for me I like to use that for that and then also for me like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53:04&lt;/strong&gt; · I use github issues so if something&apos;s high-priority like I kind of I use the github mobile app as which is really nice to be able to like keep track of like okay what are the issues that are the the most important right now so that&apos;s not like Anna keep and kind of prioritize stuff but yeah I would say note-taking on bear is one of my secret weapons so nice yeah I also think it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53:26&lt;/strong&gt; · depends on what kind of team you have to manage behind all the stuff right yeah what other tools you&apos;re you&apos;re using and I mean we recently also started notion problem you know notion as well so you can advise if you&apos;ve dropped notions great and yeah just organize everything inside one singer project basically gets rid of you know Trello and all be all the different channels like ever know whatever you use before so I think it&apos;s just like a personal choice as well and we&apos;re sure yeah best&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54:00&lt;/strong&gt; · to get along with so just how you organize yourself best but I think it&apos;s always great to hear what other people look people are using currently by the way real quick before we move on I just wanted to if you&apos;re in the video call right now please put your email down below with your name and we are going to after the call is finish like send out all but like will draw from the prizes and stuff like that so only people that are on the call itself are eligible for any rewards so just want to thank&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54:31&lt;/strong&gt; · everyone for joining so far but yeah if you&apos;re on the call we&apos;re gonna save the chat so we can do the drawing from there so make sure you just put your your name and email and thank you for registering it was awesome yeah I think there&apos;s one less question which as how how do you handle studying flutter to reach this kind of how do you handle study&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54:56&lt;/strong&gt; · yeah ride the wave I mean you know it&apos;s a I didn&apos;t become an expert day one you know it&apos;s you know joining and you know having to learn all the wrong ways to do something in flutter you know I you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:55:15&lt;/strong&gt; · you have to be I was always learning I never I wanted to try to come up with a new exciting thing like almost every day or every week and flutter and you know it&apos;s a challenge that you know you&apos;re always outdoing yourself and for me it was also just kind of like discovering you know what I didn&apos;t know and that you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:55:33&lt;/strong&gt; · know okay you know it&apos;s like I jumped into rust like I didn&apos;t know rust I didn&apos;t know how to do FFI I didn&apos;t know how to do web assembly I didn&apos;t know how to do VR and all these things are things I took on because I wanted to learn how to do them and so yeah I would say if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:55:49&lt;/strong&gt; · there&apos;s something you want to learn how to do just do it like you know just like find a way find the resources if you care enough you will fight like you will find the knowledge you will find people that are doing it you&apos;ll find podcasts you&apos;ll find documentation seek it out and you will you&apos;ll be surprised that we can come up with yeah I economy more actually and I also have to say that if what you&apos;ve seen wired Edition stuff and snuffy those are actually my first web from connect so I&apos;ve never written a web project before so I can just agree with that and I think most importantly is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56:21&lt;/strong&gt; · most important is that you have something to work on that you have some kind of motivation I&apos;m not talking about like deviation it lasts all week or something because I don&apos;t think this exists except if you have like really like like an idea that you want to push out but I think also always understand&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56:40&lt;/strong&gt; · that your you will never be the all-knowing expert you know because if you want we have that never you know try to do new things that learn new things because you will have this kind of feeling of you know being embarrassed to just go out and maybe I have something that is wrong you know you never want to get there it&apos;s just like you always want to go out and and accept that you maybe&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57:03&lt;/strong&gt; · be wrong right and just like go out try out new things and learn new stuff then I think it&apos;s also very important I mean this is a good I think closing thought from the very beginning that we have yeah it was at the beginning we said you always should also try out new things so not only try to become the very like specialized whatever fluttered deaf but also try out look around maybe there&apos;s like even other technologies that are interesting as well right so check out new things and then maybe maybe it&apos;s something else that gets you interested in yes you hope them I think this is the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57:34&lt;/strong&gt; · only way to go to always you know keep on trying out new stuff and keep this yeah yeah always be a learner cuz I think I think a mistake a lot devs make as they stop learning once they get their first job you know that&apos;s like oh well I learned everything in college to get to this point I learn the algorithms and now I&apos;m good so it&apos;s like yeah you can you can make a career only using one technology I met many people that have but I think if you want to truly excel as a developer and as a human I think&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:58:05&lt;/strong&gt; · always learning your entire life will youwill you&apos;ll discover things about yourself he didn&apos;t know we&apos;re possible yeah and except that you&apos;re pulling up the best you start something new I mean you won&apos;t be the best that&apos;s for sure [Laughter]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:58:32&lt;/strong&gt; · there&apos;s so much stuff out there in the internet if you just go ahead let&apos;s go ahead and real quick before we we close up which we&apos;re about to do let&apos;s go ahead and take one more photo so if you want to go ahead and turn on your video we&apos;ll make sure to get that before we leave and thank you so much for everybody putting your emails in the chat like we said earlier if you case you missed it me and Fredrik are gonna be sending out some promo codes for both widget studio and wire - and maybe some&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:59:04&lt;/strong&gt; · other interesting things so stay tuned but yeah we just want to thank you so much for you know coming with us today and this Meetup this was a really fun for us I know it was really fun for me and I&apos;m pretty sure Frederic can say the same but you know we were really passionate in the community and we really enjoyed being able to be a part of it and just hopefully you know we can give you a pathway and a story on how you can also build tools for the community specifically yeah I really&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:59:46&lt;/strong&gt; · enjoyed it as well I think it was a great evening as well I really liked also listening to your ideas and you of course I mean it was also the first time I you know we didn&apos;t rehearse interaction so it was super interesting and yeah let&apos;s see where this goes and yeah absolutely all right come on all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00:09&lt;/strong&gt; · you shy people do it just turn it on high you taking the picture now I&apos;m waiting I&apos;m waiting for some of these stragglers there we go come on a couple will provide a photo for you okay right all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00:36&lt;/strong&gt; · right count down from seven seven six five four three two one so much everyone yeah this was a really really impactful and I&apos;m really glad we got to talk so much and thank you so much for your questions of course as always you know you can you can follow us on Twitter we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01:01&lt;/strong&gt; · both you know we&apos;ll make sure it&apos;s linked and you&apos;ll see it on stuff but we want to say thanks again to code magic because they you know they&apos;re kind of the reason why this is all possible and make sure to check them out they they&apos;re like one of the only solutions for flutter that are completely targeted to you know allow you know releasing from an independent solution so yeah okay&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:01:27&lt;/strong&gt; · yeah and thank you so much and we hope to see you guys soon - thank you so much for joining us have a great night yep have a great night everybody thank you so much bye-bye talk to you soon&lt;/p&gt;
</content:encoded></item><item><title>Material You: Applying dynamic color to your app and brand</title><link>https://rodydavis.com/videos/flutter/material-you-applying-dynamic-color-to-your-app-and-brand/</link><guid isPermaLink="true">https://rodydavis.com/videos/flutter/material-you-applying-dynamic-color-to-your-app-and-brand/</guid><description>Material You introduces a new level of individuality to app interfaces, providing tooling and guidelines to help developers visualize and implement dynamic color changes while maintaining brand consistency.</description><pubDate>Wed, 27 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Material You: Applying dynamic color to your app and brand&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;4bguZJwHqsQ&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Material You is enabling a new level of individuality across interfaces. But how does dynamic color interact with the distinct brand expression in your app? We dive into the NEW tooling for visualizing dynamic color and seeing those changes reflected throughout your app&apos;s UI.&lt;/p&gt;
&lt;p&gt;Resources:
Material Theme Builder → https://goo.gle/material-theme-builder-web
Material Theme builder available for Figma : https://goo.gle/material-theme-builder-figma
M3 Design Kit : https://goo.gle/m3-design-kit&lt;/p&gt;
&lt;p&gt;Speakers:
Ivy Knight, Rody Davis&lt;/p&gt;
&lt;p&gt;Watch more:
Watch all the Android Dev Summit sessions → https://goo.gle/ads21-allsessions
Watch all the Jetpack Compose, now with Material You sessions → https://goo.gle/ads21-materialcompose&lt;/p&gt;
&lt;p&gt;Subscribe to Android Developers → https://goo.gle/AndroidDevs&lt;/p&gt;
&lt;p&gt;#AndroidDevSummit #MaterialYou #Featured #Latest&lt;/p&gt;
&lt;p&gt;product: Material Design - Material You; event: Android Dev Summit 2021; fullname: Ivy Knight, Rody Davis; re_ty: Publish;&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi, I&apos;m Rody Davis, a developer advocate for Material Design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:11&lt;/strong&gt; · IVY KNIGHT: And I&apos;m Ivy Knight, a designer advocate for Material Design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · RODY DAVIS: Our team focuses on helping the community build beautifully with Material Design by providing resources, tooling, and examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:21&lt;/strong&gt; · IVY KNIGHT: Material design is a design system created by Google, backed by open source code to help teams build high quality digital experiences for Android Flutter and the web.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:30&lt;/strong&gt; · It includes an extensive library of components that can be customized to meet your style and brand needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · Building off Material Design&apos;s primary and secondary colors, Material Design 3 introduces tertiary and additional color slots to check for accessibility and ensure harmony.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:46&lt;/strong&gt; · This year at I/O, we showed the bold and expressive evolution of Material Design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:50&lt;/strong&gt; · We&apos;re excited to be here to show more of Material You with dynamic color to show what it is and how to implement it in your own app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · Material You reimagines color as a more individualized experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01&lt;/strong&gt; · So what is dynamic color?&lt;/p&gt;
&lt;h3&gt;What is dynamic color&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:03&lt;/strong&gt; · The Android S color values can be selected algorithmically through dynamic color extraction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · With dynamic color, custom palettes are generated based on an individual&apos;s wallpaper choice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:14&lt;/strong&gt; · Dynamic light and dark schemes can manifest throughout an individual system UI and in certain apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · Schemes will be changed or adjusted based on preferences and vision needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:25&lt;/strong&gt; · Dynamic color is an algorithmic system that supports individualized color experiences, while also honoring color that connote brand identity or conventional meaning, such as green for Go or red for Stop.&lt;/p&gt;
&lt;h3&gt;How does it work&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:38&lt;/strong&gt; · How does this work?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39&lt;/strong&gt; · Well, starting with the user&apos;s wallpaper, a source color is extracted and extrapolated into five key colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45&lt;/strong&gt; · Each key color is then interpreted into a tonal palette of 13 tones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:50&lt;/strong&gt; · And a selection of colors from the tonal palettes are then slotted to a group of specific roles and values that map to components called a scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · Color schemes can be considered a cohesive group of relative tones rather than a fixed group of constant values.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:06&lt;/strong&gt; · Light and dark scheme is generated for each.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:09&lt;/strong&gt; · Schemes will be changed or adjusted based on preferences and vision needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:13&lt;/strong&gt; · Air color is also slotted automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:16&lt;/strong&gt; · Each color role required in UIs is derived from the key colors using this process, using proper contrast between elements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:24&lt;/strong&gt; · These roles are what you will map to in your designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:28&lt;/strong&gt; · You may be familiar with the current 12 color spots, like primary and on primary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:33&lt;/strong&gt; · With Material Design 3, or M3, we introduce a new tonal palette in roles and schemes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:40&lt;/strong&gt; · Container and its respective on container, used for UI elements that do not need as much emphasis as their non container counterparts, and a new tonal palette, tertiary, used to bring broader color expression in your product.&lt;/p&gt;
&lt;h3&gt;Tokens&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:53&lt;/strong&gt; · RODY DAVIS: How do we make this all possible?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:55&lt;/strong&gt; · Well, it&apos;s through tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:57&lt;/strong&gt; · This allows you to change your role assignment that cascade consistently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:01&lt;/strong&gt; · Building with values that change at runtime means we need a reference colors by semantic meaning instead of hard coded values.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:08&lt;/strong&gt; · Building off of the Material Design 2 color roles, tokens provide global styling slots.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:13&lt;/strong&gt; · A token can be multiple types, paired with the value or referencing another token.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:18&lt;/strong&gt; · With Material 3, we now have the concept of pallet, reference, and system tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:23&lt;/strong&gt; · The color roles you build with are system tokens, and they can inherit from reference tokens, which we&apos;re introducing with the tonal pallets, which include primary, secondary, tertiary, neutral, neutral variant, and error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:38&lt;/strong&gt; · Design tokens enable flexibility and consistency across a product by allowing designers to assign an element&apos;s color role in a UI, rather than a set value that are handed off to implement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:49&lt;/strong&gt; · Design tokens can be generated, saving the developer and designer time by agreeing on a single source of truth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:57&lt;/strong&gt; · A developer can then reference this file to map to the theming object in Compose, for example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:02&lt;/strong&gt; · If these tokens are changed in code, this can now be shared back with the designer to have the values updated in their designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:08&lt;/strong&gt; · Swapping colors and typography hard coded values with tokens should make it a lot easier for you to iterate on your designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:15&lt;/strong&gt; · Colors in a tonal palette are mapped to light or a dark scheme through design tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:20&lt;/strong&gt; · The color scheme values can be overridden to inherit from custom colors or other reference tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:25&lt;/strong&gt; · User-generated color utilizes these color mappings with tokens to create dynamic and expressive UIs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:32&lt;/strong&gt; · When the system colors change at runtime, they update the tonal palettes, which then update the color schemes, which is what you use to map to your theme and components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:40&lt;/strong&gt; · It is important to use the right color rules for the color spawning component to ensure accessibility and continuity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:47&lt;/strong&gt; · But how can these tokens on their own ensure accessibility?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:50&lt;/strong&gt; · IVY KNIGHT: Since color schemes are defined by tonality, rather than hue or a hex value, the system of tonal palettes is essential to making any color scheme accessible by default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:00&lt;/strong&gt; · So color combinations between elements based on luminance will meet accessibility standards as a result. For example, these colors may have a different hue to them, but the tone is very similar, making contrast inaccessible, especially for those with certain color blindness.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:16&lt;/strong&gt; · And here&apos;s the same concept applied to components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:19&lt;/strong&gt; · The colors are guaranteed to be accessible, because of all the pairings have a 60 luminant spread.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:25&lt;/strong&gt; · Dynamic color lets personal devices feel personal by incorporating personal preferences in the UI of your product.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:32&lt;/strong&gt; · Users will have more control than ever over their devices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:35&lt;/strong&gt; · With the dynamic color and M3 color schemes, your app&apos;s colors automatically adapt and integrate with your user&apos;s colors by letting their wallpaper influence the app&apos;s color scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:45&lt;/strong&gt; · We understand this is a new way of thinking of colors for your app, but our digital products we create can now reflect trends in our real life products that adopt to more personal colors, patterns, and materials.&lt;/p&gt;
&lt;h3&gt;Creating accessible color palettes&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:58&lt;/strong&gt; · Sometimes, we just need some colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:00&lt;/strong&gt; · If you don&apos;t feel comfortable coming up with your own color palette or lack the resources, dynamic color provides a way to have ready-made, accessible color palettes that your user will love.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:09&lt;/strong&gt; · We understand that your brand color schemes may need to be the focal point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:14&lt;/strong&gt; · But with the lightest color system, it will still be accessible and fit into the overall color story, so this is all possible with your own brand and design system colors, creating harmonious, accessible color palettes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:25&lt;/strong&gt; · M3 supports systematic applications of custom parameters that help define and maintain your brand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:32&lt;/strong&gt; · This custom color scheme can be used as a fallback for Android apps that have dynamic color turned off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:37&lt;/strong&gt; · Either way, to migrate and take advantage of Material 3 within your Android app, you will need to first migrate to baseline or M3 custom scheme to access the new tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:47&lt;/strong&gt; · Or we always encourage you to utilize material design, and expand as needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:52&lt;/strong&gt; · The color system can be integrated with custom components and brand styles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:57&lt;/strong&gt; · The color system is designed to automatically handle critical adjustments that provide accessible color contrast, legibility, interaction states, and component structure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:07&lt;/strong&gt; · RODY DAVIS: The first step in migrating to Material 3 is referencing the new tokens and connecting them to your components in your app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:14&lt;/strong&gt; · The topography, shape, and color files should feel very similar to Material 2.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:19&lt;/strong&gt; · Make sure to grab the latest baseline or custom branded scheme and set the values.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:24&lt;/strong&gt; · There are a few new tokens to look out for, such as primary, secondary, and tertiary container, as well as on variants.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:31&lt;/strong&gt; · This could be quite laborious, so we created a tool to generate these for you, called the Material Theme Builder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:36&lt;/strong&gt; · You can open it up on the web and click on Custom and then export for Compose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:40&lt;/strong&gt; · If you have any custom colors, they can be added as extended colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:45&lt;/strong&gt; · We could start by opening the tool and navigating to the Custom tab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:48&lt;/strong&gt; · In the Material Theme Builder, you can identify and input one or more brand colors that will be used to define your palette.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:55&lt;/strong&gt; · Adding specific colors determines how each tonal palette is generated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:59&lt;/strong&gt; · Here, you can input the key colors for your brand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:01&lt;/strong&gt; · Each color should be assigned the appropriate corresponding key color role depending on its purpose in the UI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:07&lt;/strong&gt; · If you have an existing app, you can use the Material 2 colors for primary and secondary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:12&lt;/strong&gt; · You can export the code on the top right and select Compose in the dropdown.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:16&lt;/strong&gt; · You can then take these files and drop them directly into Android studio and update your package imports, if needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:23&lt;/strong&gt; · This will update the colors, typography, and theme files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:27&lt;/strong&gt; · With the code updated, you can now run your app to see your new branded theme reflected in the components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:33&lt;/strong&gt; · To explore dynamic color, you can use the web tool to preview various schemes generated from a source color or an image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:40&lt;/strong&gt; · Next, we&apos;ll talk about how to update your app to use the colors generated from a user&apos;s selected image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:46&lt;/strong&gt; · Open up the theme Kotlin file and add a check to see if you&apos;re using dynamic colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:51&lt;/strong&gt; · You can then return the light in dark colors for a scheme created by the tonal palettes from the system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:56&lt;/strong&gt; · With the code added, you can now run your app on a device and change the wallpaper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:00&lt;/strong&gt; · It will now show the user-generated colors for your theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:04&lt;/strong&gt; · You can add a toggle for the user to choose between dynamic or custom themes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:08&lt;/strong&gt; · Both of these will have light and dark schemes that the user can choose from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:12&lt;/strong&gt; · Dynamic color can be applied selectively to work alongside a branded color scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:16&lt;/strong&gt; · For example, a profile or account screen can reflect an individual user&apos;s color, making important moments feel individualized.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:24&lt;/strong&gt; · If you have semantic colors, they don&apos;t have to be left out and will be included in as additional colors ready to be implemented.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:32&lt;/strong&gt; · This means you can use the dynamic color for your app&apos;s main theme and include extended colors for semantics or even brand colors, or maybe have an extensive library of colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:41&lt;/strong&gt; · The extended colors will still go through the same tonal process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:44&lt;/strong&gt; · IVY KNIGHT: With both dynamic and branded themes, Material You allows you to build beautiful expressive apps with color that reflects the user&apos;s choice and tokens that help developer and designer collaboration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:54&lt;/strong&gt; · We&apos;re excited to see what you build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:56&lt;/strong&gt; · For more on dynamic color, you can find the Material Theme Builder on the web, or install the Figma plugin, which works with the updated M3 design kit, available through the Figma community to visualize dynamic color and create custom color schemes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:09&lt;/strong&gt; · And of course, check out material.io for the material three guidance and links to all the resources mentioned.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:16&lt;/strong&gt; · You can find us at @RodyDavis and @margeeta on Twitter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:19&lt;/strong&gt; · Thanks, you all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:20&lt;/strong&gt; · RODY DAVIS: Thanks, you all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:21&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Chat With Lit #1 - Westbrook Johnson (Adobe)</title><link>https://rodydavis.com/videos/interviews/chat-with-lit-1-westbrook-johnson-adobe/</link><guid isPermaLink="true">https://rodydavis.com/videos/interviews/chat-with-lit-1-westbrook-johnson-adobe/</guid><description>Lit hosts Adobe&apos;s Westbrook Johnson for the first episode of &quot;Chat With Lit,&quot; discussing web components, the Spectrum project, and the state of the web components community.</description><pubDate>Sat, 24 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Chat With Lit #1 - Westbrook Johnson (Adobe)&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;it-NXhxkOJo&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Chat With Lit is the Lit team&apos;s live-recorded Twitter Space series hosted by Rody Davis (@rodydavis) and Elliott Marquez (@techytacos) from Google.&lt;/p&gt;
&lt;p&gt;This is the first Twitter Space featuring Westbrook Johnson (@WestbrookJ) from Adobe.&lt;/p&gt;
&lt;p&gt;Follow the Lit Twitter account at https://twitter.com/buildWithLit to join in live at the next Space!&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:01&lt;/strong&gt; · cool i see westbrook join uh can you hear us okay that seemed to work awesome yeah sorry for that everyone it uh totally just did not work as expected even though we scheduled it but awesome um super excited to invite everybody to this first lit space we have some cool topics&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:24&lt;/strong&gt; · and an awesome guest um westbrook from adobe would you mind introducing yourself yeah hi everybody i&apos;m westbrook johnson and i am a web components aficionado a lover and developer and sometimes hair pull router i work with adobe where i lead the spectrum web components project but i am also a part of a couple really cool projects both the open wc and modern web were&lt;/p&gt;
&lt;h3&gt;About Westbrook&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · a really great group of open source projects and developers as well as the web components community group which is a newer initiative to bring together web components developers across all of the frameworks and base classes so that we can have a you know a bigger voice in the broader javascript community oh that&apos;s really cool um so how did you start with the working group um yeah could you tell us a little bit more about that uh well it&apos;s important or i i think it&apos;s&lt;/p&gt;
&lt;h3&gt;Working Group vs Community Group&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:30&lt;/strong&gt; · kind of important i think the w3c maybe thinks it&apos;s a little more important to to differentiate between working group and community group uh but i don&apos;t actually know the full difference other than we don&apos;t write specs but the community group got started just before the end of last year a bit of a response to a really&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:53&lt;/strong&gt; · thoughtful blog post that went around around the the promise of web components by leah vareau and it started a really great conversation about you know are we as a community really coming together to to tell the story and of the benefits and the tribulations and the possibilities of web components in a way that sort of aligns with the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:22&lt;/strong&gt; · bright future that was maybe sold to the community you know over the last eight or ten years depending on how long you&apos;ve had your ear to the ground on web components wow that&apos;s really cool um yeah i know it&apos;s been a cool group to kind of be a part of and kind of help with all of that um so how did you discover lit um what&apos;s like kind of your history with using lit and what you use it for and what you like about it yeah&lt;/p&gt;
&lt;h3&gt;Westbrooks Lit Experience&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:50&lt;/strong&gt; · so i&apos;ve actually used lit for a while now i actually i got started with web components early on in the polymer project days uh right before 1.0 actually and so i&apos;ve been following it for a good long time i started working in applications and at the time when lit first came out i was working on a video chat application and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:19&lt;/strong&gt; · sort of pairing it with things like firebase and some of the sort of last vestiges of the polymer library i found really great success and it&apos;s been really cool seeing the ease with which doing things like that has increased as the community has really expanded and the sort of tooling foundation to you know both develop as well as test as well as release and maintain has expanded&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:48&lt;/strong&gt; · yeah for sure i uh yeah i&apos;ve liked lit because of just like how small it is it makes it really easy to you know kind of build these like reusable blocks and i didn&apos;t actually didn&apos;t use polymer i actually joined right when uh right when lit was a thing oh yeah uh you were were you working with web components before or nope i actually joined right as web components were actually officially supported on all browsers so oh you you&apos;re like a sweet summer child there yeah exactly&lt;/p&gt;
&lt;h3&gt;Web Components&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:23&lt;/strong&gt; · uh yeah so maybe we should do a little bit of a introduction for reading cool yeah so my name is rhody i am a developer advocate for material design uh and then i&apos;m elliot i am a software oh sorry sorry we&apos;re in the same room we&apos;re finally working from the office maybe we can figure that out in a bit but uh yeah i&apos;m elliot i&apos;m a software engineer on the uh i was let&apos;s say polymer team on the lit team and material design as well um and uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:54&lt;/strong&gt; · yeah cool and uh could you introduce yourself one more time with for anybody that just joined yeah i&apos;m westbrook and i am a lead developer on the spectrum web components project at adobe awesome very cool so another thing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:14&lt;/strong&gt; · we could talk about is maybe maybe ways than you were kind of utilizing some of let&apos;s uh maybe most recent features and that can kind of be up to you but just curious um yeah what you&apos;re what you&apos;re using right now that uh maybe people might be interested in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:34&lt;/strong&gt; · yeah uh that&apos;s a really exciting question because there are so many new features coming to lit 2.0 however it&apos;s also a little bit of a like i&apos;m about to get their question in that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:53&lt;/strong&gt; · spectrum web components supports a lot of as yet to be released but rather large projects and at their size it&apos;s a little difficult to move to the rc that that that lit is on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:09&lt;/strong&gt; · but we&apos;re starting to look at a number of the patterns um even in like sort of bridge implementations one of one of which is the the context uh sort of resolution via uh the directives um and and really hanging that off of elements via the um uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:34&lt;/strong&gt; · the name just jumped out of my brain via the the controller api oh yeah the reactive controller the reactive the the reactive controller api which uh makes it really easy to to both situate a context in your tree&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:49&lt;/strong&gt; · but also um you know educate your component how to resolve its context uh yeah throughout the tree yeah speaking of do you prefer writing typescript with this or you use javascript and if you use typescript do you like using the decorators so i do use typescript and it makes it really cool to use the decorators because i think that that sort of approach is really really smooth and i really hope that the relatively&lt;/p&gt;
&lt;h3&gt;decorators&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:20&lt;/strong&gt; · sun shiny news i&apos;ve heard from uh from the ecmascript body about decorators maybe gets that into the main body fingers crossed like i don&apos;t know if i had like 10 more fingers i&apos;d cross them five more times oh yes for sure&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:39&lt;/strong&gt; · uh oh i&apos;m circling back just a little bit with uh with uh reactive controllers um it&apos;s i feel like it&apos;s one of the lit two and i was wondering like is there any cool use case that you&apos;ve actually been using them for in uh either at work or personal projects or anything of sort&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:01&lt;/strong&gt; · yeah so i&apos;ve i&apos;ve used them uh for a couple in my research via personal projects for what will be um hopefully actual features at adobe in the future uh one place where i i really like it is i&apos;ve used it to build my own slide deck&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:24&lt;/strong&gt; · and pair it with uh some uh some animation uh libraries to be able to make the slides animate really beautifully with uh with physics not just with css animations&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:41&lt;/strong&gt; · yeah it&apos;s pretty cool uh i was uh uh talking to one of our uh material web engineers and uh liz if anybody knows her and uh she was actually doing this really cool thing on how to like automate accessibility per role by making like a controller for like combo box or controller for like big button and stuff like that um i&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:03&lt;/strong&gt; · just think i don&apos;t know i really think it&apos;s a cool idea like uh controllers to like declaratively add all these features and bundle them up so really excited to see where that goes yeah the rest of the community takes it yeah i think that&apos;s that&apos;s really cool at adobe are one of our not lit client side&lt;/p&gt;
&lt;h3&gt;modularity&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;9:25&lt;/strong&gt; · projects has done a lot of work with with modularizing things like accessibility features for use across multiple contexts like that and we&apos;re we&apos;re really excited for once we were able to get to lit uh 2.0 so that we could start looking at using that similar modularization strategy for both for accessibility features but also some of the shared state contexts that come up really really often in a component library&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:55&lt;/strong&gt; · like a spectrum my components but also like in the application context that we&apos;re leveraging spectrum web components yeah speaking of theming do you have a preferred method for styling your web components yeah ah well it&apos;s interesting i have some preferred opinions um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:20&lt;/strong&gt; · that i that i might use if i were to be developing from a project from scratch uh and i would love to be able to get this into spectrum web components a little more often things like css parts and the like but we&apos;re actually at a really interesting [Music] location in the spectrum spectrum is adobe&apos;s design system the spectrum ecosystem wherein&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:45&lt;/strong&gt; · there is another ui client-side javascript ui framework that outputs spectrum components and both of our projects consume a pre-existing css library that outlines um the uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:05&lt;/strong&gt; · the spectrum design system for web if you will and that system is structured all around css custom properties and it actually uses a really great sort of central set of tokens to be able to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:24&lt;/strong&gt; · populate the the entire component eco system uh with whether you&apos;re talking about dark versus light themes or you&apos;re talking about large versus small scales or also we do a lot of work to ensure that we support right to left as well as left to right text and the css custom property system that they&apos;ve set up is the basis for that in our application&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:53&lt;/strong&gt; · that&apos;s pretty cool talking about styling have you had any uh interesting uh applications for different types of like you&apos;re using css custom properties as like the basis i was wondering if you have any interesting use cases but for like uh shadow roots uh i mean the shadows um for what is it called uh part uh like colon colon part uh for selecting certain things are y&apos;all mostly staying away from that or is that for like one-offs yeah so&lt;/p&gt;
&lt;h3&gt;adding parts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;12:24&lt;/strong&gt; · the the broader system as i said is structured before it becomes part of our project so including parts in the system is not something that it would it would involve us deviating from the larger spectrum community uh but one place where we have just brought parts into the library i feel like you&apos;re like watching my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:50&lt;/strong&gt; · github is uh we have a we have a theming system so that uh that allows us to switch on as that sort of core set of css custom properties uh and so it&apos;s an element that takes the the scale and the color stops&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:09&lt;/strong&gt; · and the text direction and then decides what css custom properties should be available to the page at that time oh that&apos;s interesting yeah and so it works really well like if you&apos;re in an app and you&apos;re not trying to do anything like defeat css layering or uh css clip uh border clipping uh but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:33&lt;/strong&gt; · almost every application we have has some sort of overlays and they every overlay has to escape those things to be displayed correctly and so we&apos;re we&apos;re throwing that part of the dom to the end of the body to ensure that we&apos;re getting rz indexing and and what not correct when that happens but when we do that we&apos;re actually throwing the content outside of our of our theme and so we kind of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:03&lt;/strong&gt; · resolve the theme context before we throw the the content and then we throw the content and when the content gets to its new location it wraps itself in a new um team scope but some of our users&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:18&lt;/strong&gt; · had found that if they were creating their own css custom properties hang off of the initial theme that the main application was in those custom properties we&apos;re not following the the thrown content to the end of the body yeah and so we we realized that uh this is actually the perfect place for park because you could assign a theme part to this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:45&lt;/strong&gt; · this theme provider that we&apos;re creating in the overlay context and be able to just use uh overlay colon colon part theme and app colon colon part theme and any of your custom css custom properties there&apos;s probably a better way to say that then apply to both places whether it&apos;s your main application or this thrown content and it&apos;s it&apos;s it&apos;s looking like a really nice way to be able to build the app specific&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:15&lt;/strong&gt; · customizations inside of the spectrum specific application that we&apos;re helping ship um i i do remember when uh part was under development that there was a pair to it uh colon colon the theme and essentially that was essentially a shadow part that would uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:36&lt;/strong&gt; · cascade through shadow roots similar to custom properties and how they do that um i was wondering uh from what i remember its current state is uh nobody&apos;s really working on it right now but they haven&apos;t really shut the door on it i was wondering do you think that could fit anywhere inside your current setup or something of the sort i you know i whether it fit inside of my setup or not i think is a little&lt;/p&gt;
&lt;h3&gt;web components community&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;16:03&lt;/strong&gt; · unclear but if that were something that was to start to land i would definitely want to find a way to do so because i think there there&apos;s this really interesting combination when you get in that i found when you get into component libraries and pattern ecosystems where you want to be able to use pieces over&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:27&lt;/strong&gt; · and over again but you don&apos;t always want them to be components they don&apos;t always need to be components and because of like and right now that&apos;s like our easiest way to create a movable piece of of ui but if we were able to have that theme that pierced through the whole shadow&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:50&lt;/strong&gt; · down stack and pointed to these specific parts all throughout your ecosystem you could have these smaller dom snippets that had visual mobility and and equality without having to nest always lean on a full custom element to do so um and i think&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:12&lt;/strong&gt; · that uh you know that touches a little bit on something that that we&apos;re going to be looking at in the next couple of months with web components community group and that&apos;s that the the one sort of responsibility for the w3c of a community group is to publish reports on the community and uh we want to bring together a list&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:35&lt;/strong&gt; · of pain points and a list of sort of orphan specs and a list of maybe future uh requests as a as a broader web components community not necessarily just lit but there&apos;s definitely a lot of lit voices in the conversation uh and uh and present that sort of unified front to the working group and the actual browser and vendors so that we can maybe whether it&apos;s theme and starting the conversation again about that or it&apos;s a new specification that gives&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:07&lt;/strong&gt; · similar or better maybe even capabilities around theming uh so definitely check out the the community group we have a slack channel and and join the conversation if you are also a fan of uh do you have a good way to for people to join that like a super short link or wow i i how does this sort of radio setting is very interesting uh and so i i can i will share something&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:40&lt;/strong&gt; · out as soon as yeah we can always uh pin a tweet or send something out later yeah it&apos;s like a follow-up this is a like brand new uh uh realm for me my first space by the way thanks for having me and ours too yeah thanks for joining yeah i mean uh i guess the show notes are just our twitter thread so [Laughter]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:05&lt;/strong&gt; · yeah um yeah like we said we&apos;re definitely going to put everything on slack and everywhere else so make sure to stay tuned for that um any final things you want to say about lit um so it&lt;/p&gt;
&lt;h3&gt;no magic&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;19:21&lt;/strong&gt; · it was interesting like thinking about like being a part of this conversation today i i was i was running into a problem uh in one of my components and i was like oh man i&apos;m gonna have like a bunch of negative energy going into this conversation and then i i i realized one of like my favorite things of web components in general but also very specifically to lips and that&apos;s that there&apos;s very little magic and i dug in and what i had been using&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:52&lt;/strong&gt; · as uh a decorator i just rewrote quickly as a getter setter pair and i was able to track the origin of like a property being double set that i couldn&apos;t figure the source of and it was just really great when i was like stop just like clicking around and like console logging around and realize i have full control&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:17&lt;/strong&gt; · over the the context that i&apos;m i&apos;m writing in there&apos;s there&apos;s no black box there&apos;s no compile to function calls there&apos;s no magic really and it made it made me realize that even when i&apos;m having hard days you can get right to the source really quick and that feels really good yeah it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:41&lt;/strong&gt; · really great to hear um i&apos;m sorry uh but like i feel like that&apos;s one of the hard line difficult type ropes that we have to walk where it&apos;s like provide enough developer tooling but make sure you don&apos;t like lose the note you know like yeah you wanna you wanna make sure that people still know exactly what this is doing and uh but then again we don&apos;t want people writing just dong calls everywhere right um but it&apos;s really good that uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:09&lt;/strong&gt; · uh you picked up on that and enjoyed that not that lit can&apos;t have magic we do uh make sure that we write things in such a way that you can change you can change the renderer to react if you really wanted to and use reactive element uh under the hood and stuff like that and write your jsx if that&apos;s what you want um or uh or just use relational in and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:31&lt;/strong&gt; · of itself so it&apos;s pretty good um i guess one thing i wanted to ask you is uh what is your favorite component you&apos;ve come across that you feel like it&apos;s just uh fun to use and work with whether one you built or when you found the community wow uh that&apos;s a a great question name your favorite child right now sam it&apos;s definitely sam&lt;/p&gt;
&lt;h3&gt;favorite component&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;21:59&lt;/strong&gt; · so it&apos;s hard to say one component unless you&apos;re thinking like an application component for me uh but uh one i think component family maybe if that helps that i think really she does a great job of of both approving&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:18&lt;/strong&gt; · the benefits of web components and and i apologize it&apos;s not a lit project but it&apos;s as close to lit as you can get because it is just vanilla web components and that&apos;s the generic components by uh by one of my fellow open wc members&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:38&lt;/strong&gt; · pascal&apos;s ship oh yeah gotta love gotta love get a little open wc and pesco yeah um but uh his generic components project uh works to just have the simplest implementation of some uh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:55&lt;/strong&gt; · of the sort of gnarly accessibility patterns that would come up on the web as web components with just really simple paths to customizing their visual delivery and they&apos;re all vanilla and they&apos;re all super small and we recently attempted to challenge twitter and i think we&apos;re on twitter uh to maybe start a bit of like a style&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:18&lt;/strong&gt; · stage or a css zen garden of customized implementations of it so if you&apos;re a big fan of styling and a big fan of uh really cool components uh definitely check those out very cool yes and uh it&apos;s all about finding web components wherever they are and whatever they&apos;re built with because that&apos;s one cool thing about this ecosystem is once you create it you can just share it with other people disintegrate it&lt;/p&gt;
&lt;h3&gt;outro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;23:46&lt;/strong&gt; · yeah absolutely well i just wanted to thank you so much for joining us westbrook and also elliot um and everyone that joined in to our first twitter space sorry for all the troubleshooting at the beginning but uh yeah sometimes things don&apos;t work as expected but yeah please feel free to reach out to us and let us know what you thought if you have any questions and of course if you ever want to hear more about lit definitely reach out thanks again westbrook and hope to see you on the web yeah thanks&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:17&lt;/strong&gt; · for having me bye&lt;/p&gt;
</content:encoded></item><item><title>Rody Davis: Building Adaptive UI/UX in Flutter</title><link>https://rodydavis.com/videos/flutter/rody-davis-building-adaptive-uiux-in-flutter/</link><guid isPermaLink="true">https://rodydavis.com/videos/flutter/rody-davis-building-adaptive-uiux-in-flutter/</guid><description>Rody Davis discusses building adaptive UI/UX in Flutter to ensure cross-platform compatibility across diverse devices like watches, TVs, desktops, and mobile phones.</description><pubDate>Wed, 13 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Rody Davis: Building Adaptive UI/UX in Flutter&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;P1B52fRGjbE&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;When developing with a cross platform framework there are new problems you are faced with that you do not get when designing for native or web. When we build UI we need to create adaptive experiences that go from a watch, passive modes on a TV or voice only, mobile devices, desktops and everything in between. It takes extra work but if you follow these patterns you will save a lot of time later.&lt;/p&gt;
&lt;p&gt;Rody&apos;s links:
Adaptive UI Demo - https://rodydavis.github.io/adaptive_ui
YouTube - https://www.youtube.com/rodydavis
GitHub - https://github.com/rodydavis
Twitter - https://www.twitter.com/rodydavis
LinkedIn - https://www.linkedin.com/in/rodydavis
Medium - https://medium.com/@rody.davis.jr
pub.dev - https://pub.dev/publishers/rodydavis.com/packages
Creative Engineering podcast: https://rodydavis.github.io/creative_engineering/
https://www.rodydavis.com&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · i&apos;m going to hand over to roadie thank you how&apos;s it going yes thank you yeah and not only um desktop and web but also we now have vr and some other things i&apos;ll be going on later so let&apos;s go ahead and get started um so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:16&lt;/strong&gt; · what i&apos;m going to be talking about today is building adaptive ui ux building for every device um this you know when we&apos;re working with flutter applications um as with most of us we kind of came from you know a mobile background so or at least some of us did and then other people came from web others from desktop um but because of that you know the tools that we&apos;re given maybe only are optimized for one so how do we build in a way that targets multiple screens so that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:41&lt;/strong&gt; · what we&apos;re talking about today if you want to follow along you can go to this url and there&apos;s the demos that you can run in the browser as well as the source code that you can have and of course they can we can have a link to this in the chat below and this is the github&lt;/p&gt;
&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:00&lt;/strong&gt; · so what is the problem well first of all when you&apos;re working with flutter applications and cross-platform frameworks in general you&apos;re going to have you know your application run on a variety of context you have you know a laptop with a keyboard that&apos;s attached to it you have an ipad which is primarily a touch input but now has a keyboard and a trackpad option and some that only have a keyboard option or with an apple pencil input you have you know watches that have just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28&lt;/strong&gt; · like a two inch touch target you have phones that vary in so many different sizes you have you know large screen form factors for for desktop you have even small netbooks which are still desktop forms but still have you know a small screen you have things like the pixelbook which has a high dpi but still a smaller screen so you know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:50&lt;/strong&gt; · you can&apos;t really predict in all the different ways that your application is going to be running as well as you know we have two in one computers that you know we they may start out one way while your application is running and change during the context and more recently we have passive computing where you can actually you know talk to your application without even using any ui at all or even some minimal ui so for example like this is um you know the lenovo smart screen or&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:17&lt;/strong&gt; · if you&apos;re working with alexa or google and just the all the possibilities that your application can be opened up to as well as something that people may not think about it&apos;s like well you know why do i need to care about you know a passive computing experience well what about if someone can&apos;t see your application and they are they need some kind of accessible uh way to access it well one thing you can do is make your app work in this kind of passive way where you can have this interchange with the user&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · without displaying any ui so like i did an experiment with my tesla app where i use dialog flow to take all the actions that you can use to control the car and then based on a string input or voice input you could actually control a car through voice this is all in a passive context so for the people that could see really well i gave a chat mode a cool&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:08&lt;/strong&gt; · different way to be able to control your car and also um you know we have folding phones now like which we didn&apos;t before we have things like the microsoft surface neo coming out and you know the samsung folding phone and there&apos;s the razer fold there&apos;s so many different uh types that we didn&apos;t expect as well as you know notches and whatnot but i think flutter does a really good job so far with building us the fastest ui toolkit that we can use to create these experiences so&lt;/p&gt;
&lt;h3&gt;The Solution&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:40&lt;/strong&gt; · first thing that you may do when you&apos;re building an application is you may start writing just like okay here&apos;s my desktop application here is my mobile application here&apos;s my you know my special admin application and you start to really like um silo your application across different projects uh the problem with this is you know you start having so much duplicated code so what are some&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:05&lt;/strong&gt; · things that we can do to fix this first of all we need to create a screen to stand on its own an application should be able to be dynamic to the point where our screens are almost like you models and they only change based on the things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:22&lt;/strong&gt; · that we provide them we don&apos;t want to have these so much business logic inside the screens that then we can&apos;t use that widget in another context because you know i used to do that all the time but then i realized that my screen just may go in so many different ways that i wasn&apos;t expecting when i originally built it next we we only want to use the break points that the parent gives so this is you know with the constraint&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:47&lt;/strong&gt; · kind of layout and flutter instead of using the bottom up approach we could just say okay i&apos;m going to give my my child this max size and then the child what it does is it just tries to be as big as possible and will change its layout depending on what its parent is the nice part about this is you can create this really complex layout that is it&apos;s self-responsive&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:10&lt;/strong&gt; · internally so you can have you know a master detail scaffold with a nested master detail scaffold with a nested master detail scaffold allowing you to have like multiple column layouts for having these list views but then as you change the screen size you can get a mobile and tablet size experience&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:30&lt;/strong&gt; · next we also want to send events up and date down i this is pretty um you know apparent but uh you know we may not think about this when we&apos;re first building we just may want to start with set state and we start to have this kind of almost spaghetti code and what you want to do is you know redux does this pretty well um except it&apos;s only a one-way data flow&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:51&lt;/strong&gt; · but here we just use callbacks to send okay this this thing is pressed and then i&apos;m also presenting this data if you do it this way this is very much the kind of view model approach but it allows you to reuse a widget because it&apos;s just a dumb widget it can it can be in whatever context it wants to be in and you can wrap it in a smart widget which is um you know has the loading the network library but the the widget itself that you&apos;re wanting presenting you want to have be as reusable as possible&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:22&lt;/strong&gt; · as well as we want to save our breakpoints in a single location uh this is also something i had to learn the hard way is you may just want to like set okay let me just set a constant for tablet breakpoint and you go to another file then you have that some other different types of breakpoints and then you start to like really mix and mingle um all your break points but if you have a either a class or just a single place to put that it&apos;s really nice to be able to modify them all at once and we&apos;ll see later on why that&apos;ll be important as well&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;6:50&lt;/strong&gt; · so here&apos;s just a simple example we have a mobile view on the left and a tablet view on the right things that you may not think about when you first built this is okay well the listview is shared across both of them we have a detailed screen that&apos;s only present on the tablet version&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:07&lt;/strong&gt; · and on the mobile one we&apos;re going to push to that detail screen the floating action button is going to be a different location but looks like the app bar and all of this stuff are still the same so looks pretty good um but i think we can do better so what what&apos;s the code that actually does this first of all we have just a simple um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:26&lt;/strong&gt; · layout builder that will on the tablet breakpoint return the tablet size and a mobile size for the other one but what are the trade-offs with this approach because there are some advantages to doing it this way and we&apos;ll go over those so the cons are&lt;/p&gt;
&lt;h3&gt;The code&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:41&lt;/strong&gt; · that you have this duplicated ui code every time that you&apos;re you know you&apos;re writing this it&apos;s going to start to really duplicate like your screen because you&apos;re going to have two different list views and yes you can internally um you know save those out but there can be start to be states that your app can be in that you may not expect um it&apos;s really hard to refactor so like if you want to you know change the listview for example you have to change it multiple places and maybe the way that the selection context um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:12&lt;/strong&gt; · and stuff like that uh different ui states so like if you&apos;re on the mobile screen and you&apos;re on an ipad for example and you resize up to a full screen then it&apos;s gonna have a different state because it&apos;s gonna have a different item selected as well as it&apos;s hard to test because mainly because the duplicated ui code so what are the pros&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:32&lt;/strong&gt; · to this approach because there are actually pros um first of all you have separate functionality if you did want to have like okay this is my you know desktop screen and i&apos;m just gonna optimize it for desktop and nothing else and then doing the same thing for tablet mobile it&apos;s a quick solution because you can just set a breakpoint and then return a new screen it&apos;s a clearly divided ui meaning that this is desktop this is mobile this is tablet and also you can you can kind of start&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:59&lt;/strong&gt; · to sort out like the advanced and basic features so you can have almost like flavors to your screens that you&apos;re wanting to present so what is an example of an adaptive layout so first of all an adaptive layout is going to change dynamically depending on the&lt;/p&gt;
&lt;h3&gt;What is an adaptive layout&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;9:19&lt;/strong&gt; · parent that it&apos;s being provided so here we have a list view that will you know navigate to the the details screen but then on a tablet version we&apos;ll select that item in the list view so let&apos;s see that here we can tap on it and then great um so but how do we build this because um right here we&apos;re only using one list view and only one detail screen so how do we make it work on both so this is where i&apos;m coming up with&lt;/p&gt;
&lt;h3&gt;Adaptive Lists&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;9:47&lt;/strong&gt; · adaptive lists and if you&apos;re coming from ios this is should sound pretty familiar because this is what the master detail scaffold is all about what we want to do is pass the selection context up meaning that when you tap on the item in the list view instead of doing something right away we&apos;re just going to send it up to the top of the widget and let the the parent decide what to do with that tap we want to push to the detail screen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:12&lt;/strong&gt; · when we&apos;re on a mobile device but then we want to show the detail screen when we&apos;re on a tablet device as well as we want to have a builder for the selection because we want to return the detail screen with new data each time that a new item is selected as well as we want to you know we the detail screen doesn&apos;t really care about the break points of the parent it only has its internal size that it um it is displayed upon as well as the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:40&lt;/strong&gt; · adapter screen can take multiple options for layout so it can be used in other places we can you know just literally drag and drop this thing wherever we need to as well as we have one detail screen and one list view this is really important it means that we don&apos;t have any duplicated code so let&apos;s just look at some basic code to um see what we would do in this context for this adaptive list&lt;/p&gt;
&lt;h3&gt;Basic Code&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;11:05&lt;/strong&gt; · um so as before you know we just have our our parent widget but let&apos;s let&apos;s look down so first of all we have the default use case which is our adaptive list which shows we want to you know not automatically imply the leading but that&apos;s for the app part we want to just when it&apos;s on a mobile setting we just want to navigate to the detail screen pretty simple all we have to do is pass a selection context of the contact that is um selected and then push but then for mobile what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:35&lt;/strong&gt; · we do is check to see if it&apos;s greater than tablet and then on the um you know for 300 pixels of the screen we&apos;re gonna have the adaptive list uh which will have a floating ax action button location changed uh which we can modify and then we just updating a value notifier with the new selection and then on the rest of the screen what it is just a value listimal builder which takes that selection context and just you know shows the detail on the screen with those contacts keep in mind um the detail screen here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:06&lt;/strong&gt; · is the same one but just with uh different settings and so the nice part is you know we don&apos;t have to duplicate anything everything is nice and dry next i want to talk about interaction ui because this is something else that changes across uh when you have a cross-platform framework so first of all um you know desktop is going to have a&lt;/p&gt;
&lt;h3&gt;Interaction UI&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;12:31&lt;/strong&gt; · different experience than web and web is going to have a different experience than mobile and you even have these weird you know kind of crossovers where you have like a mobile web experience which is kind of different than the native experience um and you know some examples of that are like you know on ios you expect to see like an ios alert dialogue on web you expect to see like web alert dialogs and then on desktop you know it&apos;s depending on what you want&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:56&lt;/strong&gt; · to do but um what i mean is it&apos;s not the same experience everywhere it&apos;s customized for the platform you&apos;re running on so uh one nice thing that i&apos;ve always done in my applications that i try to right out of the gate is just try to build for cupertino in addition to material because if you&apos;re doing this and for those that are not aware cupertino is the apple and ios style guide for creating these&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:21&lt;/strong&gt; · widgets which flutter has a great you know library of them but by doing this you let your ios users feel at home as well as your your android users now of course there&apos;s other design languages too and you can implement as many different ones as you want but um some easy ones that you can get right out of the gate is any time that a flutter widget supports dot adaptive definitely use it so like any time you have a switch or slider or i think there&apos;s even like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:48&lt;/strong&gt; · switch list tiles and stuff like that and all these allow you to have internally under the hood the cupertino for apple devices and then material for everywhere else we want to change the snack bar location this is another easy win but like you know like on a desktop you don&apos;t want to have the snack bar be like the full width of your screen that would be kind of crazy instead you want it to be just you know a small normal size snack bar in a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:12&lt;/strong&gt; · specific location same for the web you may also have a different behavior on web where you don&apos;t want to just to show it and dismiss it you want to let the user close it where on a mobile device you do want to have it dismissed and so there&apos;s just like little nuances that we got to be aware of as well as we want to have a a full screen dialog versus a pop-up so like on mobile device we want to you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:32&lt;/strong&gt; · know just show a full screen dialog we don&apos;t have enough space to be able to do much more than that but then on on a tablet we want to have like a dynamically resized pop-up that possibly can be dragged around because this is something that the user is expecting and if they don&apos;t see that they&apos;re just going to see all this white space and be kind of turned off to that as well as we want to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:53&lt;/strong&gt; · take advantage of the extra space just as before because you know we have you know it&apos;s you know when you&apos;re building this application for desktop you know people can have their monitors really big and there&apos;s so much white space that just it makes your app look empty and unfinished so um&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:10&lt;/strong&gt; · you need to look to ways to find how to fill that space in a way that makes sense and also is good for the user and you can also show a lot more on the screen so especially with the latest in material design there&apos;s a new density layout you know key that you can add that allows your your ui to change accordingly so like if you&apos;re um for example on a desktop you can have it more dense but then on a tablet less dense&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:36&lt;/strong&gt; · as well as like we can think about you know ways that we can multitask so like on desktop we can actually show multiple windows um as well as the web you may your app may live in multiple chrome tabs or you know regular tabs and it&apos;s something that mobile doesn&apos;t necessarily have but is still a very important behavior for your application and if you&apos;re curious i do have a demo on how to do the multi-window thing you can check it out on my github but it is possible with flutter and it&apos;s just it&apos;s another way to take your application to the next level&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:07&lt;/strong&gt; · so let&apos;s look at like just the a poor experience on the tablet so first of all when you&apos;re when you&apos;re on a tablet view you&apos;re just going to you know have a um a full screen pop-up and it&apos;s just going to take up the whole screen and this this looks really bad so um&lt;/p&gt;
&lt;h3&gt;Tablet View&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;16:22&lt;/strong&gt; · let&apos;s just see what that does under the hood well first of all we just have a settings icon um which then will show or how do we fix it actually rather this is we&apos;re going to have a dialog that&apos;s going to show an adapted dialog with our settings screen in this this adapted dialog will actually internally wrap um our responsiveness for it and our fallback is always going to be just showing a full screen dialog of the settings screen so let&apos;s see what that experience would look like well we have a a nice settings dialog here and uh&lt;/p&gt;
&lt;h3&gt;Demo&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;16:55&lt;/strong&gt; · like i said earlier all these demos are available on that link but we have like a nice responsive uh modal dialog that will then if you if you go too narrow or too shallow it will actually go full screen again as well as if you start with a mobile device it will stay in a full screen context this is pretty powerful because it allows us to use existing widgets and screens in a way that makes sense dynamically because you know just because it starts off in a a desktop you know view size&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:25&lt;/strong&gt; · doesn&apos;t mean it&apos;s going to end up there this is just an easy win that we can do so the adapter the adaptive dialogue under the hood is actually pretty simple and what we do is we just check to see if either side is less than our break point we want to&lt;/p&gt;
&lt;h3&gt;Adaptive Dialog&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;17:40&lt;/strong&gt; · return the child otherwise we want to center that with the dialog size that we we have predefined with an aspect ratio widget um and in the child which will then um shrink it down to where we need it so this is really powerful it allows us to have just a simple dialogue we just pass a child in and we can make anything responsive like this next we don&apos;t want to forget about dark mode yeah&lt;/p&gt;
&lt;h3&gt;Dark Mode&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;18:06&lt;/strong&gt; · it was it was fun trying to figure out how to ways to support this before flutter officially did but um yeah this is something that is really important because you know you may think well i&apos;m never gonna use my app and dark mode like light mode all the way but um the the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:22&lt;/strong&gt; · the side the other you know argument to that is you know if you have trouble seeing something your app needs to perform in you know high contrast settings um and one of the ways they do this is usually they invert the colors and so you know this is very important because we need to make sure that we&apos;re not building for dark mode just because people want dark mode settings but also for accessibility reasons so that people can you know use your app in as many ways as possible and the user should be able to control the setting but it&apos;s not always required&lt;/p&gt;
&lt;h3&gt;System Mode&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;18:52&lt;/strong&gt; · flutter now has a system mode theme mode setting where you can actually set it to system and then basically if the device has dark mode on it will automatically switch the dark mode theme or dark theme that you have in material app otherwise it&apos;ll go to light theme so these are both you know very important context it&apos;s just a quick demo where you can have you know just the colors if you look and see like like the reds are just slightly muted and we have different hover effects as well as shadows so um yeah definitely add dark mode&lt;/p&gt;
&lt;h3&gt;Platform UI&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;19:25&lt;/strong&gt; · next we also want to build for platform ui so um in addition to interaction ui we have platform ui and this is like so uh mac os linux windows ios android and web these are the specific contacts and domains that they exist in and how do we build for them this could also extend to things like you know if we&apos;re building applications in vr or if we&apos;re building applications in the watch how do we these are all the platform specifics that we have to fix and we definitely want to make each&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:58&lt;/strong&gt; · one feel at home so uh one simple thing that a lot of you run into when you first run your application the web you&apos;re like wow everything that i you know my application is running but then like you know why don&apos;t i have like urls um to be able to like link to anything and that&apos;s because you have to set up your application to support um these various contexts so one nice&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:17&lt;/strong&gt; · thing is if you use named routes on flutter or on generate route you can actually take advantage of this so if you&apos;re using everywhere navigator pushed manually material page route in your application you&apos;re not going to be able to take advantage of this put all that logic into on generate route and you&apos;ll be able to handle this you there&apos;s also something that&apos;s kind of like different on the web that you may not be logged in when you go really deep into a screen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:44&lt;/strong&gt; · so like you know you may have a huge url user you may need to lazily load the user in something that may be different than mobile and of course this depends on the application but just you know just be aware of it as well as you know on desktop we want to support menus&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:00&lt;/strong&gt; · you know this is also really powerful it allows you to have a file context to be able to like you know support keyboard shortcuts shortcuts for that but also just you know the user some users just love browsing menus to see what all the application can do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:15&lt;/strong&gt; · when you have something selected or you know just seeing like a quick way to create a new file so one easy way to do this is to use the menu bar package that&apos;s in desktop embedding and then you can just literally do like command in and then now you have a shortcut for creating a new file like if yours is a document application so this is also pretty important&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:35&lt;/strong&gt; · as well as we want to you know mobile we want to support url schemes so like you know if our application runs on both the web and mobile you know if they click on something and it&apos;s the same url scheme it&apos;ll actually open up the mobile or desktop application so this is pretty powerful we also want to support native apis as well as you know using established patterns out there and you know like i said before for apple devices you want to support cupertino when possible because this allows the users to feel at home&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:04&lt;/strong&gt; · and the same goes true for material design you want android users to feel at home because not every user knows exactly where to click and you may have this fancy design on dribble but then the user has nowhere i know where to touch and uh that&apos;s just that&apos;s something we want to be very mindful of so here&apos;s just a simple example we have the settings dialog but we have material on the left and cupertino on the right and if you see none of this is actually duplicated we&apos;re using just adaptive stuff under the hood and it allows you to use the same&lt;/p&gt;
&lt;h3&gt;Native Widgets&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;22:36&lt;/strong&gt; · widgets in both contexts one of my very early packages that i made for flutter was called native widgets which did this under the hood i um i wrapped all kind of widgets and did both the platform implementations under the hood so this is just an example of something you can do to you know reuse and make your application feel at home wherever possible&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:57&lt;/strong&gt; · adaptive ui doesn&apos;t have to be spaghetti code i know it can seem like a lot but it&apos;s really just about finding a pattern and sticking to it because you know things are going to change um you know new paradigms are going to come and go new devices are going to come and go but it&apos;s about building your your application in a modular way that you can you know compose it together so i think that&apos;s really important as well as we want to have like dynamic layouts um you know we want to build a layout that the user can control so think about something like vs code for example you know every time&lt;/p&gt;
&lt;h3&gt;Finding Patterns&lt;/h3&gt;
&lt;h3&gt;Dynamic Layouts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;23:29&lt;/strong&gt; · you open up a a file in vs code you don&apos;t just have like a predefined location the user can drag that around and put it in separate you know tiles they can make multiple windows they can have multiple tabs and it&apos;s all up to the user and this is where i want to talk about giving control to the user for the layouts you know we build responsive layouts and we have to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:51&lt;/strong&gt; · predict in as many ways that they can be used but then also we can also hand that control over to the user to then let them take it even further because a user is always going to have some window layout that&apos;s going to make sense to them that not every user is going to like and this way you can cater as many people as possible we can also let each window have a full screen context and the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:14&lt;/strong&gt; · example i&apos;m going to be showing is a golden layout library that i found that um i recently rebuilt for flutter so let&apos;s look at this you can go to their website i&apos;ll have the link on the repo but basically golden layout is this thing that existed for web and it&apos;s a it&apos;s a window tiling library there&apos;s other frameworks that do this too but it allows you to take a tab and be able to you know resize it to any place you can put it down in any location you can close tabs you&lt;/p&gt;
&lt;h3&gt;Golden Layout&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;24:42&lt;/strong&gt; · can maximize tabs and it&apos;s really powerful um so one thing about this is like just like vs code like the the application doesn&apos;t set in a rigid way where all the windows can go this allows the user to based on their device screen and see it even supports pop-up windows but based on their device screen they can customize it however they want&lt;/p&gt;
&lt;h3&gt;Golden Layout Demo&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;25:05&lt;/strong&gt; · so how can we achieve this in flutter so like i said i created a library called golden layout and it does exactly this it allows you to in a very quick way be able to let the user you know specify where they want their tabs they can drag it anywhere they want and of course this is a i think the package was released like less than a week ago so there&apos;s lots of stuff to be able to be improved but you see you can drag a tab from any one tab to another tab and this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:33&lt;/strong&gt; · of course is using like a tree data structure under the hood but you can resize both vertically horizontally they all can handle uh wherever you want to have the tab live in and of course you can quickly close them there&apos;s even ways to prevent a tab or window being closed but i just want to show you this is this is just a simple way with just like just widgets under the hood to be able to give your layout anything so if you have like a complex desktop layout or like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:01&lt;/strong&gt; · enterprise application that has a bunch of dashboards i would definitely suggest looking at this library because this will let the user have total control and you can kind of take away like some of the guessing game that goes into creating a complex layout put it on to individual windows and let the user just select those windows and then a nice way is they can kind of build their own layout as they make sense to them and of course the nice thing about this library too is the fallback is it opens&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:28&lt;/strong&gt; · up everything in a new tab so by default you just have like one big screen with a bunch of tabs across the top just like a regular web browser so even if the user doesn&apos;t want to have multiple windows they can still have a very reusable way because the tabs are scrollable so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:44&lt;/strong&gt; · another thing another package that i recently built was storyboards and why would we want to do this first of all well we want to be able to build all your screens at once i know when i&apos;m developing application you may start with you know the login screen and the home screen settings screen and then the about screen and you have like four different screens you&apos;re going back and forth between them and sometimes you can like start to forget about screens you forget that you may have changed your you know theme methodology you may have changed like the way things look over here um and then just in terms&lt;/p&gt;
&lt;h3&gt;Storyboards&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;27:15&lt;/strong&gt; · of consistency you need to be able to watch them all at the same time so uh one thing you do is you know edit your theme and watch all your screens update at once so this is really nice um with support with material design you can just change the primary color and then everything changes as well as you can work with dummy data so i i have the ability for you to add&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:35&lt;/strong&gt; · custom screens and custom lanes to be able to say okay i want this you know widget to be shown in a testing way to show this dummy data so then that way you can you can have your protected screens but then also show them in this dynamic way as well as you know it supports all your routes in your applications so this is just another nice developer tool as well as we can change our constants in one place and observe what breaks so um you know css&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:04&lt;/strong&gt; · you know would have this too where you can you know just change one line of css and it&apos;ll change some stuff but uh you know you can go to your content file and then because it supports hot reload you know all your devices will change so you can change like your your break points and then you can see what flexes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:20&lt;/strong&gt; · as well as you can test on different devices at once so you can have the same screen and multiple screen sizes so you can make sure that you want to support specific specific sizes and make sure it works looks good on there so uh one thing for me was you know always having to test on the iphone 5 because at the time my boss had an iphone 5 and so that was always a pain as well as you know being able to support uh restart so i actually have a button now that will restart your application which is pretty nice so let&apos;s look at&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:48&lt;/strong&gt; · example uh here is the flutter gallery app running on a storyboard as you can see on the top app bar it actually picks up your theme and the storyboard will change depending on what you have there so like all your colors and your fonts and everything so what&apos;s nice about this is see you can you can start to pick up so many things that you wouldn&apos;t have been able to pick up when you&apos;re testing your application especially if you&apos;re like a qa engineer and you need to be able to test multiple screens at once and say okay are all these things look correctly&lt;/p&gt;
&lt;h3&gt;Storyboard&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;29:18&lt;/strong&gt; · you can do widget tests you can do all kinds of stuff because this way you can basically multi-thread your testing if you wanted to but it allows you to you know make sure that you know everything looks correct uh because when you change something you have to go okay let me go to the login screen let me go to the about screen let me go to the settings screen and of course you can do gold and file testing too what you need to be doing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:41&lt;/strong&gt; · and all that but this is just a nice way when you&apos;re building something really quickly you can have it all up at the same time and of course the link for this will be in the description so something else i want to show you real quick before my presentation is up and this is my flutter editor i&apos;ve been working on if you&apos;ve been following me on twitter maybe even seeing a little bit of this but um making a ton of progress on it so if you want to try it out for yourself it&apos;s flutterweb.app and there&apos;s the link there so let&apos;s go ahead and check it out so one interesting thing that i&apos;ve been&lt;/p&gt;
&lt;h3&gt;Flutter Editor&lt;/h3&gt;
&lt;h3&gt;Widget Studio&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;30:11&lt;/strong&gt; · working on is a widget studio allowing you to have you know a a storyboard kind of context for you know creating widgets as you can see the cursor is actually changing and this is because under the hood you can change depending on if you have the spacebar or command key selected as well as you know you have you can change your icons you can have a pwa manifest generator as well as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:32&lt;/strong&gt; · being able to open up the project directly in vs code so this is all running on the web and creates a real flutter application so um let me just let that gif restart but yeah what&apos;s cool about this is it allows you to work really quickly in a prototyping way to be able to add multiple screens and edit your files on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:53&lt;/strong&gt; · desktop it actually can compile your flutter application but yeah here we see we&apos;re using a native color picker which is really nice and it&apos;s just like a really you know dynamic way here we can easily drop and create widgets with just you know really quickly which is really cool and then you can even control context like when this button is tapped launch the screen show this dialog and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:16&lt;/strong&gt; · all that kind of stuff is supported so yeah like i said make sure to check it out in the link below so what&apos;s the conclusion well first of all you got to embrace the change and just know that the devices we have now are not the devices we&apos;re going to have in the future things are going to be&lt;/p&gt;
&lt;h3&gt;Embrace the change&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;31:33&lt;/strong&gt; · different you know we&apos;re going to have a new device that&apos;s going to come out next year that we&apos;re not predicting and it&apos;s going to change everything we think about design and layout and then you know things may also change you know like there&apos;s different you know there&apos;s desktop neo for example is a really cool desktop thing that looks into the future to see like how can we work with you know experiences fuchsia is another example you know with um desktop and how do we change the way that you know we build our you our uis like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:00&lt;/strong&gt; · because it doesn&apos;t have to be static there are the thing is also we don&apos;t want to just like alienate our users because something changes we want to make sure that whatever we&apos;re doing makes sense is not too drastic but is always making progress so first of all create a pattern and stick to it this is really important&lt;/p&gt;
&lt;h3&gt;Create a pattern&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;32:20&lt;/strong&gt; · whenever i start a flutter application i will whatever pattern i&apos;m choosing at the time like for right now i&apos;m i always have a very specific folder structure i have a um two different types of libraries that i go from i either go with a a uh what&apos;s it called a flutter more and um and basically&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:42&lt;/strong&gt; · shared preferences approach where i have a relational kind of data as well as i also have i&apos;ve been using more recently freeze and provider in addition to value notifiers to be able to have this dynamic way and it all depends on the data first of all but whatever pattern you stick to make sure to go and finish your application with that pattern it&apos;s always you&apos;re always able to refactor but i would suggest waiting to refactor until after you&apos;ve released the application your app can get this like infinite cycle of just constantly changing it and i know&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:12&lt;/strong&gt; · because i do this all the time but is if you wait until after your release then you can refactor write your tests make sure that whatever your refactor doesn&apos;t break your existing stuff as well as we want to extract widgets and components wherever possible um a pro tip is if you were building your applications build it with as much ui on the single screen as once without using methods only after you finish the screen then start extracting because it&apos;ll start to get really complicated with all the callbacks going up and down&lt;/p&gt;
&lt;h3&gt;Extract widgets&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;33:41&lt;/strong&gt; · but yeah definitely do this because that way you can then use your widgets in wherever you want a good rule of thumb is your only import should be flutter material or the the core library so dart math and stuff like that because if you do and you only support those not only can you you know reuse them but you can make sure that they are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:03&lt;/strong&gt; · logic less meaning that they don&apos;t depend on anything else besides flutter and then also you can use you know you just pass the data and then let it present we also want to send all the actions up if we want to reuse the screen this means passing up all the contacts for you know on tap on long press whatever we want to support for that widget&lt;/p&gt;
&lt;h3&gt;Reuse the screen&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;34:22&lt;/strong&gt; · as well as we want to pass down the data so this means every time that the data changes we want to have the widget rebuilt as well now you can have intermediate widgets that are smart and do the logic like i said before like if you have a widget that just wraps up the stream builder and does everything we want to make sure that um that is done in a good way because&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:44&lt;/strong&gt; · when you when you have that that layer and that separation then you have like a kind of view model approach where you know you can change out the widget without having to change out the model and the logic as well as we want to write dry ui code because as with everything the more code that we write that is&lt;/p&gt;
&lt;h3&gt;Dry UI code&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;35:01&lt;/strong&gt; · duplicated the harder it is to test and the more places that we got to change it so make sure that when you&apos;re doing this you can build your application in such a a faster performant way that make sure that we&apos;re not you know duplicating anything as well as you know just building an application&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:16&lt;/strong&gt; · that can run in multiple contexts you know like i said before not everybody builds it the cross-platform mindset in mind but this is important because we have such a powerful toolkit which is flutter under a hood and if we&apos;re not like taking advantage of it then why are we using cross-platform framework if you&apos;re only using flutter to build like an ios application or an android application and i feel like you&apos;re only taking like advantage of like five percent of what it can offer you so um make sure to uh go to this link if&lt;/p&gt;
&lt;h3&gt;Outro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;35:44&lt;/strong&gt; · you want to see the examples make sure to follow me on twitter github and of course you can reach out to me on email um and thank you so much and uh yeah and if you have any questions let me know excellent thank you very much rhody very very interesting stuff you&apos;ve been working on i&apos;m sure you&apos;re a part machine yeah definitely he&apos;s the hero&lt;/p&gt;
</content:encoded></item><item><title>60 - Flutter with Rody Davis</title><link>https://rodydavis.com/videos/interviews/60-flutter-with-rody-davis/</link><guid isPermaLink="true">https://rodydavis.com/videos/interviews/60-flutter-with-rody-davis/</guid><description>An interview with Rody Davis details Flutter, a high-performance, mobile-first framework designed for cross-platform development of iOS and Android applications.</description><pubDate>Mon, 07 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;60 - Flutter with Rody Davis&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;gcU80lHHslI&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Alex interviews Rody Davis about Flutter, a cross platform framework for iOS and Android development. They also address cross platform development issues Dropbox experienced with their mobile apps.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · you&apos;re listening to inside hours dev show about real-world iOS development today with you your host Alex Bush and today we have a very special guest Rhodey Davis hi Brody hey how&apos;s it going good good so Rhodey this episode is sort&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:17&lt;/strong&gt; · of sort of on the wings of the last episode but we would talk to road even before then basically Rhodey you you already started in Florida college as a starting something sound engineering and then worked as a system administrator and then later as a freelance producer and developer and and these days working&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:41&lt;/strong&gt; · as a full-stack developer building cross-platform applications iOS and Android and using flotter specifically so this is why we have Rhodey here today to kind of talk about it since last episode we talked about Dropbox announcing that they you know dropping&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:02&lt;/strong&gt; · their support of C++ cross-platform before we talked about react native and my experience with Java scaring code with JavaScript so now we figured okay let&apos;s talk about flutter so Rhodey I guess stage is yours and tell us what&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · flutter is let&apos;s start with that awesome well thanks for having me on so one of the biggest things about flutter as compared to other ones this flutter is a mobile-first framework it wasn&apos;t ported from the web it was never meant to have web compatibility actually the whole reason flutter was made was they took chrome and they said let&apos;s take out all the web dependencies and see how fast we can make it and so that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:46&lt;/strong&gt; · was actually the very first prototype version of flutter since then they were able to do Android and then later iOS what I&apos;d note flatterers Witten and dart right not jump yep flutter is written in Dart so the very first early version of dart was actually very similar to JavaScript it didn&apos;t have types since then when flutter was released they really start to point O which is strongly typed so one of the cool things dart is it has two modes has ahead of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:16&lt;/strong&gt; · time compiling where it actually compiles all the code down to machine code but the other one is just in time compiling where it actually can allow hot reload this is while you&apos;re coding your application you can make a change and it just inserts the code while instead of having to do a full rebuild now since then Xcode has come out with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:35&lt;/strong&gt; · previews on swift UI so it&apos;s kind of very similar except for the fact that it&apos;s actually running your application when you do a hot reload and flutter it actually maintains your app state so if you&apos;re 10 applicate out of ten screens down it you don&apos;t have to go back to and you&apos;re just still on that same screen you make a change it I just updates so it&apos;s really powerful for a development and speaking of that development tools for flutter have been just simply awesome Google is really good at developer first focused stuff so yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:06&lt;/strong&gt; · and another yeah another side note real quick about the flutter stuff specifically is for me in our application at our company we&apos;ve been able to support all the way down to iOS 8 and Android KitKat so with the same UI so which has been super awesome so ok let&apos;s quickly take a step back so sort of just to reiterate to you big port parts of the whole flutter echo system is again dart right is the objective oriented hi what is a general-purpose&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:39&lt;/strong&gt; · language with type system and optional dynamism or other way around dynamic optional type type safety and then there is flutter which utilizes the language and flutter itself is a framework or what is it virtual machine&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:00&lt;/strong&gt; · it&apos;s a framework so you actually ship it with your application so what&apos;s nice is you can even add slaughter to an existing app as well so yeah you you can actually for iOS when you create a flutter project it will do iowa&apos;s as a framework and then on android it&apos;ll be a module of flutter as well as long as you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:22&lt;/strong&gt; · it connects it correctly you&apos;re able to get access to all the stuff so the difference between react natives and flutter is react native uses of webview flutter uses a uiview so on flutter you&apos;re actually painting all the pixels to the screen so instead of just rendering objects over a virtual JVM on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:44&lt;/strong&gt; · flutter you just you&apos;re literally painting everything inside that canvas so you could actually have an existing iowa&apos;s application and just have one section of your screen or just an entire screen that&apos;s flutter okay yeah so we&apos;ll get to that how it renders it so but essentially the way to use it right you either as you mentioned either can have one portion of your app maybe one screen can it be just one view even yes right it&apos;s just a UI view basically you initialize your eye again maybe you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:15&lt;/strong&gt; · could clarify how technically it happens but you initialize your flutter quote-unquote component and then it spits back out a view canvas that is gonna draw a draw on and then you insert that into your view hierarchy yes and the UI views and flutter communicate with dart over what are called platform&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:35&lt;/strong&gt; · channels and so it&apos;s basically JSON that it goes back and forth so you can for example if you have a Bluetooth plug-in it will stream the Bluetooth events over to Dart but then you can also stream events back to the native platform so there&apos;s a lot of plugins and flutter where we&apos;re starting to do things like platform inputs where you can take an ios text field and then get access to them from the dark side as well but typically just to finish with how it all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:01&lt;/strong&gt; · kind of initialize them and how you work with it from the outside on the higher level perspective in general though you would have one flutter produce one big container view that takes over your entire app and that effectively overlays code on core everything becomes your app that flutter code drives correct okay that makes sense and so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:30&lt;/strong&gt; · yeah how does it draw it so you mentioned that unlike react which is basically sort of transpiling that 3/3 virtual three who uiview stuff or something like that how does flutter you said it draws it so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:49&lt;/strong&gt; · does it mean the rebuilds the implements all the UI components we have yes actually in fact they took every single quick to Cupertino widget and I&apos;ve rebuilt it in footer which is awesome so you can make an app look exactly like iOS but the really powerful part about flutter is you can take on any branding or theming that you want you can make it look like material design you can make a literary iOS you can make it look like completely custom and because flutter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:14&lt;/strong&gt; · was built with UI and animations first you always have 60 frames a second so like I said where flutter came from they took chrome the rendering image it engine called skia and that&apos;s actually where they get the painting library from so all like they have all the codes that they use on chrome - you know parse HTML and JavaScript you create websites they use that same I think it&apos;s written in C++ rendering engine to use for flutter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:41&lt;/strong&gt; · so it&apos;s super high performance and extremely fast and then so all it does is take a UI view or an Android an Android view and just paints all the pixels coming from flutter flutter has its own tree and can do its own dipping and stuff for changes but the thing is since it&apos;s compiled down the machine code it doesn&apos;t have to do that when it&apos;s running only when it&apos;s um debugging mm-hmm so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:03&lt;/strong&gt; · does it mean that there is a typically a delay between iOS and Android releases and let&apos;s say they come up with new UI elements or something and then slaughter team has to recreate them copies roughly speaking the really cool part about flutter is the community has been super awesome one of the most active communities I&apos;ve been a part of and a lot of times community will make the thing before even the flutter team does and then just submit a PR and it&apos;ll be pushed out on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:34&lt;/strong&gt; · the next version of flutter but you can you can making widgets is super easy so if I wanted to make a custom text field you get access to everything in the framework everything down to skia is written in dart so if I wanted to make my own canvas renderer from a flutter or if I wanted to make my own custom gesture recognizers I can do that because it&apos;s all written in dart and because I ship my own version of flutter it really can be as customized as I need it to be okay so so that inter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:07&lt;/strong&gt; · so you also mentioned that there is a communication between layers is in Jason right could you yes give more details on that how that process goes yeah so it&apos;s called platformer channels and so it allows letter to communicate with the native side this is only for the backend plug-in side not for UI so if you&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:31&lt;/strong&gt; · connecting with Bluetooth for example you can have your the Iowa side be an objective-c or Swift so it&apos;ll communicate with that Swift plugin over that json platform channels so very simple types you know strings json maps or you can have a list of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:48&lt;/strong&gt; · events and you can communicate both ways it allows you to have all your existing Swift&apos;s and Objective C code the same so you could literally take an application written in Swift yeah install flutter on top of it and then just communicate with all your existing Swift code and they just basically rebuild your UI and flutter if you wanted to it doesn&apos;t mean that flutter application when it needs&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:14&lt;/strong&gt; · to do networking it has to have that bridge between itself and whatever networking you nsurl session right on iOS so on Android it&apos;s equivalent so the cool part about Google being so big is the fact that flutters been internal for like six years and Plus dart has been used on angular dart for a long time so a lot of those kind of core parts of the flutter are in Dart it&apos;s the networking HTTP are is actually built into dart&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:46&lt;/strong&gt; · itself so you don&apos;t even need a platform channel if you don&apos;t want to so you can of course do that if you want to use in a crl session or you can just use dart HTTP and do pretty much everything you need to so it&apos;s more like a trying to do everything in Dart if possible in terms of code sharing and then going out to the native platform when you can&apos;t when you have to do something perfectly so for example I built a cross-platform MIDI synthesizer for flutter and by doing that I took my native Android app and my native iOS I took the MIDI synthesizer out of them&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:18&lt;/strong&gt; · and created a common shared platform channel to where I could say play MIDI 127 and then for each plug-in it would say okay if the note is this then it runs the native code and vice versa so it allowed me to in dart just call play MIDI note but in the Swift and Android I&apos;m able to call the native Android libraries and the native Swift packages as well mm-hmm mm-hmm I see okay that that makes sense so AHA I what I wonder&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:49&lt;/strong&gt; · is how kind of from my research I my understanding was that flutters design and architectural if you will model is similar to react in a sense of rear end during a tree of of your UI tree based&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:09&lt;/strong&gt; · on data changes so you have this unidirectional data flow for from like a Redux type of thing and then you calculate the diff and order not you but there yeah work with them using an algorithm and then it just updates the parts that were supposed to change is that is that how it works conceptually and and sort of when you build flutter apps that&apos;s your mental model yes exactly so flutter has its own tree but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:40&lt;/strong&gt; · the difference is it&apos;s painting everything onto the canvas so whereas on react native you would have a performance delay by going over the JVM and trying to communicate that back to UI elements for flutter it&apos;s just rendering whatever it&apos;s seeing in addition to that flutter because it&apos;s so new and because it&apos;s so extensible you can use whatever state management solution you want if you want to use Redux there&apos;s already Redux packages and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:04&lt;/strong&gt; · it&apos;s built for that if you want to do something like that&apos;s similar to combine on Swift so like very streaming kind of approach is important right yep there&apos;s there&apos;s rx start there&apos;s flutter block there&apos;s a lot of paradigms that are already built with the streaming components in mind so you have to just build on top of what Swift and Android have you can do it all completely in dark where it&apos;s made to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:28&lt;/strong&gt; · have high performance what&apos;s the recommended way like what does Google promote you know Apple keeps pushing NBC down our throats similar thing with well reactive rendering or whatever is cold so with flutter it&apos;s an open question right now because there&apos;s so many options they recommend for beginners to use provider which is a a simple version&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:51&lt;/strong&gt; · of a change notifier where you can just call like you&apos;re telling the UI when to change and then it&apos;ll update all the listeners the very simple one if you want to use something called set state which i know all i OS components are stateful by you know by default so where if you just change the field it&apos;ll just update on the UI but in flutter you have to call set state which causes a rebuild for that specific widgets but then if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:15&lt;/strong&gt; · you&apos;re using streams the next recommended one is blocks so it allows you to just react based on changes coming in hmm I see very interesting I mean the side note I used dart many years ago well three four years ago when it was still web yes stuff and it was mmm best alternate if I could find first&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:39&lt;/strong&gt; · web development with angular was pretty great yeah okay so and it changes all the time - right yeah actually now now I&apos;m curious cuz I thought they&apos;re very rigid in terms of like oh no no let&apos;s only use reacts model and I was like that&apos;s it then that&apos;s the biggest issue&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:59&lt;/strong&gt; · one of the well maybe one of the issues that I have with react whole thing is the design of it right but if they&apos;re open to two other mmm sort of solutions that that&apos;s great that&apos;s good to know provider was actually built by the community as well and they&apos;re recommending it so that&apos;s that&apos;s another example where Google is saying you know we may name may not be the best on this but we&apos;re gonna promote something in the community that is so right&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:29&lt;/strong&gt; · make sense okay so let&apos;s get to let&apos;s let&apos;s talk about that Dropbox article right maybe they&apos;re like sort of four points that they had there and maybe we can see if flutter addresses them or has them at all things like that so one of the problems they had was the overhead of custom frameworks and libraries right there C++ they had to build build a lot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:02&lt;/strong&gt; · of stuff right that to share again to simplify my understanding like networking right if they were networking in that shared codebase a C++ code base they had to build all of that and then somehow Bridget with both Android and iOS yeah how flawed or addresses that so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:27&lt;/strong&gt; · one of the biggest things with flutter is it allows you to use any existing framework so in fact you can use Carthage or cocoa pods or straight-up modules if you want on iOS you can very easily just create flutter for just iOS applications or just Android applications in fact when I started I didn&apos;t even use flutter for Android I was only using it for iOS because of how much faster I could move so for example&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:54&lt;/strong&gt; · if you need to use a a crypto currency or crypto kit that&apos;s built with cocoa pods then all you have to do is go and install that as a cocoa pod inside your framework and then you have access to it a good example of this is the plug-in system for flutters every plugin and flutter can not only just shared our code it can also do UI code for native&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:14&lt;/strong&gt; · platform channels which is what makes it so powerful I can bundle because I&apos;ve created a lot of packages for flutter I send out it can includes its own dependencies with either cocoa pods on iOS or all the Android modules on Android and when you send out that plug-in it will install everything you need to for that application flutter also knows the inter dependencies so if you have version mismatches and whatever but it allows you to leverage any&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:42&lt;/strong&gt; · existing application so for example when I created the midi synthesizer I went out to Android found an android library that was already built to do midi synthesis i customized it and then i just installed that inside of the android application and then published that for android and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:58&lt;/strong&gt; · same for iOS there was a MIDI the sizer already built in cocoa pods and so I took that and you know just built something on top of it so you don&apos;t have to rebuild the wheel every time you need to do something mm-hmm one of the big related to the issues that Dropbox mentioned was threading and a background process process yes and so the coordinate and all of that it was a challenge with C++ what happens with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:25&lt;/strong&gt; · Florida so really cool part is flutter by default is single threaded and a lot of people think that that&apos;s all dart is but dart provides a extremely simple interface on top of isolates which you know can manage background processes on both iOS and Android you can create as many cores as you need to so like let&apos;s say I spun up I spin up these isolates&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:48&lt;/strong&gt; · it will use as many cores as the phone has available so if you have an eight core phone it will use all eight core threads the only limitation with isolates is you have to day there have to be static functions or types coming in because isolates have no information&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:05&lt;/strong&gt; · about the other isolates around it so basically you have to compile the work and then you get the result back and do something with it you can&apos;t have access to other things inside of it you have to pass everything it needs basically interesting so these isolates they are it&apos;s not a concept of a thread really it&apos;s well it&apos;s built on top of Iowa&apos;s threading so oh yeah it uses the same like network or the Grand Central Dispatch and it mm-hmm yep and it uses&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:34&lt;/strong&gt; · the Android one as well but it actually the reason why it has to be static is so it has everything it needs to spin it up and whenever it finishes you just have to use the results so if you&apos;re going to do the Fibonacci sequence you just specify the however many numbers to go out to and then you just get a result back so primitive types so interesting so okay you cannot really give it a custom model&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:00&lt;/strong&gt; · object to compute stuff right you have to give it your motives yeah but you can give it a map which is really powerful so you can parse JSON on an isolate so you can give it a map data and get back a custom object so it but it then within the isolate that function you run it is still dart fully capable start so you can recreate models or whatever structures in there okay exactly the worst case you just map yes to the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:28&lt;/strong&gt; · dictionary thing and then back from that to whatever correct yeah so just JSON interesting so does it mean that you and I can&apos;t really as you said they can&apos;t talk to each other like with with let&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:44&lt;/strong&gt; · say you have something Rx swiftly type of stuff where you have a stream of data and you want to process it asynchronously and then map one thing unto another then filter then have some other hi-oh or their function and then subscribe to the whole chain to get the results on let&apos;s say main thread so the isolates are only for doing extremely heavy operations that are like under the hood there like one-off operations&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:10&lt;/strong&gt; · everything else you use a streaming API with stream controllers and stuff like that to where you can do exactly what you said where you modify the stream will pass it down and react to events flutter uses like a big Advent cycle so every time you do like a button press it just keeps computing until that event triggers in the future and then it just works off the next one so yeah flutter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:32&lt;/strong&gt; · when you&apos;re using streams and stuff you have all you can depend on anything you need to and it&apos;s exactly what you would think it would be like it&apos;s only isolates where you doing like you know like math or something that would be a very heavy calculation that&apos;s what you&apos;d use AC makes sense okay let&apos;s go to the next big point in Dropbox article so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:57&lt;/strong&gt; · they had an overhead of custom development environment meaning yeah they had Swift&apos;s and objective-c Swift iOS environment Xcode all about stuff and libraries and then they had equivalent some Android and it was all so they&apos;re ready there but then four plus plus score that they shared between them nothing was there and they had to build it all and you know polish their&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:23&lt;/strong&gt; · own tools and create their own tools and that took a big overhead was it we go ahead took a lot of work yep so a huge thing that one of the reasons why I chose flutter cuz this thing exactly so one advantage that it has over react native is when I&apos;m in you know and footer for example I personally love using BS code and it has first party plug-ins for flutter but then when I want to go into Android and let&apos;s say I need a refactor to Android X and I I can open my flutter project just the Android&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:55&lt;/strong&gt; · folder in Android studio have access to all my java my Kotlin all my packages I can refactor with the built-in IDE tools like I can refactor to Android X or not to do you know you get access to just like a regular Android app but then if I want to go to open my Xcode project I just right-click on the iOS folder and can open Xcode I have access to all my normal things that you&apos;ve seen a Xcode project I can create a storyboard I can create a you know all my my plugins my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:23&lt;/strong&gt; · dependencies I can you know even go down to my actually app delegate which can be in Swift or objective-c and you you&apos;ll feel right at home because it&apos;s an actual Xcode project so which is really nice make sense okay another another&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:41&lt;/strong&gt; · thing they talked about was the overhead of addressing differences between platforms right and I think they even had like a sort of funny to me example of was picking images or something image picker one of those you that initially&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:01&lt;/strong&gt; · was similar but then as platforms developed diverged and that kept that overhead of that kept increasing yes so one like I said I&apos;ve made a lot of packages for flutter and well we can use the image picker as an example but when&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:19&lt;/strong&gt; · you want to do things and I wasn&apos;t Android separately you just say like on dart you would say image picker pick image and then on Android it would present the native dialog to pick an image and on iOS it present an iOS dialog - like an actual iowa&apos;s I like to pick an image and then you get that image back from the result so whenever you&apos;re doing custom stuff that you want native features you would use the native components already you don&apos;t have to rebuild those if you want to you can but otherwise you know all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:50&lt;/strong&gt; · the differences are in the plugins themselves so as a plug-in maintainer I will have to write this swift and Johnny yeah but then when I publish that the person using my flutter package only ever has the dart of course they can you know do a pull request of mine and if they want to make improvements but all they ever see is just image picker pick image image picker pick video or you know stuff like that so they don&apos;t even have that part in their application that they have to worry about but okay but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:20&lt;/strong&gt; · does it effectively mean if you sort of if you rely on JavaScript package plug-in packages like that that they mostly become uncommon implement a common denominator between two platforms right they cannot really diverge too much into how iOS does it or Android because well you need to find that only one place where they overlap yeah so I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:44&lt;/strong&gt; · try to like in my applications I try to make it as abstract as possible so the only part that&apos;s going to be different is going to be the very very very last step which is gonna be the calling out to Swift to call it like an iOS delegate for something and everything else is going to be in dart for me so everything like they&apos;re receiving the parsing the networking all those other layers are gonna be inside dart so I don&apos;t have to write that twice and as far as the UI that&apos;s another area which can differ but uh since the release of dart 2.3 you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:16&lt;/strong&gt; · do if iOS and do conditional things inside of your UI if you want to show specific things on different platforms but yeah it&apos;s it&apos;s the biggest thing is going to be on the back end Swift and Kotlin differences but it says it&apos;s a shared or is separate and abstract as you want it to be or someone could put all their completely separate Iowa&apos;s networking&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:40&lt;/strong&gt; · and once a find an android networking on the other but you kind of lose the advantage of having dart and be able to already have networking and do that all in one place right hmm okay that that makes sense I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:56&lt;/strong&gt; · guess still they&apos;d like in in some scenarios and that&apos;s one of those problems with cross-platform solutions like that I am just trying to trying to think of you obvious simple and that we can talk through but maybe a lot of iOS&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:18&lt;/strong&gt; · apps they prefer using tabs but Android apps prefer using the hamburger menu with a side menu right effective function of it is similar to the user you&apos;re picking between quote-unquote menu options within your app but you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:39&lt;/strong&gt; · know but yeah if let&apos;s say you were coding that on Iowa&apos;s using Swift you can do both and there are libraries to help you or you just use a UI tab bar from Apple UI kit but then the way you structure it to complete code is so different you so how does that happen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:59&lt;/strong&gt; · with that yeah so it&apos;s actually a package I just released on flutter it&apos;s called mobile sidebar and it does exactly this it allows you to abstract your navigation because because flutter everything is a widget I mean everything even your theme is a widget you create a navigation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:16&lt;/strong&gt; · component where you can just take a list of screens and you specify what type of navigation you want and then it handles the navigation for you so for if you want a tab bar you say you know type tab bar and then under the hood flutter will create a tab bar and then put the screens for each one as a stack and then if you for Android if you want a top tab bar you can also do that too it handles it so by being able to abstract that kind of stuff you&apos;re able to in one place keep your navigation so instead of nesting everything inside one screen you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:48&lt;/strong&gt; · can you and you know abstract so you create your settings screen and you pass in setting screen into this tab bar navigation and then it handles when to call that and where to show it so it&apos;s all about because everything&apos;s a widget you can do stuff like this so you can call just setting screen as a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:07&lt;/strong&gt; · parenthesis as an object you can use that anywhere you want to you can push to it you can show it you can render it and stuff like that hmm so it&apos;s one of those things that like you have to like do an example application to see yeah then you then you really like you get it like I didn&apos;t really understand it when I first started but once you start it&apos;s really addicting because you can just create things so fast and like you can end the refactoring stuff and flutter is awesome so you create a huge UI tree and you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:35&lt;/strong&gt; · just like click on a widget and refactor out to extract the widget and then you can completely have that as a separate component so you can that&apos;s actually what flutter recommends is you build your application on one screen as big as possible or is minimal screens as possible and then you just refactor at the end and then you can put it in separate folders and stuff like that so it knows what depends on what and yeah it&apos;s that&apos;s one of my favorite parts very interesting yeah and you only need&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:04&lt;/strong&gt; · to play without specifically yeah okay and you can feel free to check out my github too I have tons and tons of examples so and you know even now with a flutter web you can actually run these examples in the browser which is cool nice yeah well we&apos;ll add that to show notes okay so the last portion of that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:26&lt;/strong&gt; · article the robox article was focusing on overhead of training hiring and retaining developers right so basically attracting talent and simply people skillful with C++ and their keys right and it was a challenge because they would either find people who know C++ which are I guess rare maybe but then&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:51&lt;/strong&gt; · they&apos;re know as much about iOS or Android and then there&apos;s that overhead of training them or other way around they&apos;ll find good iOS engineers and good Android and I only know Catalan and Java and maybe Swift or Swift an object to see they don&apos;t know C++ nor do they want to yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:12&lt;/strong&gt; · that&apos;s even more important actually bigger problem and you know you you can&apos;t blame them really because every other the likely most of the other jobs they will not it wouldn&apos;t be useful yeah exactly so we actually had our company we just hired another flutter engineer and you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:32&lt;/strong&gt; · know because flutters new obviously people aren&apos;t going to have three years a flutter experience but we did look for native developers so I found someone who is an Iowa&apos;s developer and I have on-boarded him and he&apos;s already self-sufficient and creating flutter applications in a matter of three weeks because you already know the Iowa side&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:51&lt;/strong&gt; · you know he&apos;s familiar with Xcode you know the hardest part about mobile is knowing at least one of the platforms it&apos;s really hard to learn mobile coming from web but if you start with mobile it&apos;s super easy to be like okay this is just a different way to write UI and you know it&apos;s really easy to talk through okay this is this is what this is in Xcode this is what this is in Swift in fact clutter has multiple guides for coming from iOS coming from Android coming from what and shows you exactly how to map your kind of your knowledge&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:21&lt;/strong&gt; · to where to put it hmm okay interesting so I have a sort of another my own question here or yeah I think to address maybe maybe the last one will probably time mark to wrap up since it&apos;s a Google&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:43&lt;/strong&gt; · product or Google technology we know how Google loves shutting things down right I was actually I kind of thought dark died not officially but virtually a few&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:00&lt;/strong&gt; · years ago because not like the web spark just fizzled away and then then typescript kind of you know became a got a bigger muscle there and the community as far as I understand yes well as with any Google product it&apos;s always you tread lightly but one of the huge advantages&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:21&lt;/strong&gt; · is it was open force open source from the very beginning so if Google went away tomorrow it&apos;s still convenient community maintained they even treat community engineers that are submitting fee requests as well as internal Google requests for PRS as the same so even&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:40&lt;/strong&gt; · Google engineers have to submit PRS to the main repo to submit changes so if it went away tomorrow hopefully the community would step up and still be able to take on all this stuff which we are doing I think you would just have not as many people on the salary doing taking care of issues but yes it&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:59&lt;/strong&gt; · definitely a concern but I think the community has been super strong and being able to like Alibaba for example has switched all their stuff to flutter and that&apos;s interesting they react native shop right baking so they make huge paw&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:15&lt;/strong&gt; · requests to flutter all the time and there&apos;s just been a lot of other huge companies that have jumped on board recently like Abbey Road Studios and just a lot of really cool companies Hamilton for example so a lot of other big companies are incentivize to keep flutter around so I think that&apos;s also it&apos;s like a catch-22 like you have to have the big companies to keep the big companies and I think we&apos;re starting to see a huge shift of people coming on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:41&lt;/strong&gt; · because they see that it&apos;s so fun to work with and there&apos;s so many people that you can find that can work with it as well as yeah it&apos;s actually just really enjoyable to use yeah I have to play with it again never you haven&apos;t convinced me per se but at least I&apos;m curious now because I again yeah it sounds like it&apos;s more different at least in some regards then I then I thought from react native yes which for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:12&lt;/strong&gt; · me personally okay I&apos;ll give it a shot but okay cool well Rhodey thanks for coming in and sharing your experience and kind of giving us this overview and absolutely sing what the problem straw box had and yeah guys you can reach us out at hello at inside I was dev calm tweeted us at inside I was dev and roadie dude where people can find you so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:41&lt;/strong&gt; · they can find me at my website at rody Davis comm Twitter at Brody Davis and on github at Apple educate mm-hmm yeah and we&apos;ll add all of those links to to our filaments all right thank you ot see you next time bye see ya bye&lt;/p&gt;
</content:encoded></item><item><title>Chatting with a Googler @RodyDavis ! #google #flutter #firebase #developers #ai #vibecoding Chatting with a Googler @RodyDavis ! #google #flutter #firebase #developers #ai #vibecoding</title><link>https://rodydavis.com/videos/interviews/chatting-with-a-googler-rodydavis-google-flutter-firebase-developers-ai-vibecoding/</link><guid isPermaLink="true">https://rodydavis.com/videos/interviews/chatting-with-a-googler-rodydavis-google-flutter-firebase-developers-ai-vibecoding/</guid><description>DevRel Rody Davis discusses AI models like Gemini 2.5 Pro and Flash, best practices for prompting, and building apps with Firebase Studio and Flutter during the GDG EMEA summit 2025.</description><pubDate>Mon, 07 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Chatting with a Googler @RodyDavis ! #google #flutter #firebase #developers #ai #vibecoding&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;v63OXnO5Qqs&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;As GDG organizers, ​⁠​⁠@GoogleDevelopers and ​⁠​⁠@Google communities organised a GDG EMEA summit 2025. During this time, ​⁠​⁠@shrinishdonde343 had the pleasure to interview ​⁠​⁠@RodyDavis, a DevRel for Firebase Studio at Google. The talk focused on new AI models in Gemini such as Gemini 2.5 Pro, Gemini 2.5 Flash etc. It also highlighted some trendy topics such as vibe coding and prompting and focused on how to best use tools such as Firebase studio to build exciting Apps.&lt;/p&gt;
&lt;p&gt;Rody also walks us through how easy it is getting as the time progresses to build an app using ​⁠​⁠@Firebase studio and ​⁠​⁠@flutterdev . Stay tuned until the end to hear his views about where the future of AI and app and web technologies will be in the next few years. Also do follow ​⁠​⁠@gdgberlin.&lt;/p&gt;
&lt;p&gt;Learn more about Google for Developers ​⁠​⁠@GoogleDevelopers here: https://goo.gle/40yMI2w&lt;/p&gt;
&lt;p&gt;Special thanks to ​⁠​⁠@alicjaheisig-chiarello1986  for helping us during the process and to the crew members for their wonderful support.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:01&lt;/strong&gt; · [Music] Okay. So, hi everyone and I&apos;m Shinish.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:13&lt;/strong&gt; · I&apos;m a GDG organizer here in Berlin and I&apos;m really thrilled to be here at the GDG summit in Berlin in 2025 with our special guest Roodi. Uh Rodi is a developer advocate, Flutter expert and a creative technologist at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:30&lt;/strong&gt; · uh something about GDG Berlin uh it is a group of around thousand plus members and we organize tech talks, workshops uh and related to latest technology trends and currently we are running a build with AI campaign and that is the reason we have AI as our topic today. So yeah, thank you so much for taking out the time and it&apos;s great to be here with you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · Awesome. Yeah, of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · So yeah, let us start right away. So I would like to like kick things off with asking is it your first time in IO connect in Berlin and how do you feel today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · Yeah, this is my first time in Berlin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · I&apos;ve been in Munich a couple times and so I&apos;m really excited about uh the event here. I have heard great things and uh the last couple years people seem to really enjoy it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:21&lt;/strong&gt; · Great. Great. Yeah. And um yeah, so how was your journey into tech? Like I would like to ask you because you are a Google developer, a developer advocate. So did you have interest in flutter right from the beginning or could you just tell our audience a bit about how your journey was and how you got into this domain a bit?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · Yeah, of course. So uh I grew up in a really small town and uh I was really excited about science um but I didn&apos;t know any developers. So for me, I came to it through hacking on everything, taking apart everything. Um, I was a musician and so I learned how to, you know, create um, music and things like that that involved a lot of like technical expertise, which led me to audio engineering. Uh, I was an audio engineer for a while and then got into video production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:14&lt;/strong&gt; · Uh, then into system administration. And it was after college when I found out about app development, not just uh programming but like you know building for iOS and Android. And I realized it was a creative field. So I was able to leverage everything that I did in music and audio and started creating music apps and things that were really fun to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:39&lt;/strong&gt; · And so having started on Swift as my first language and iOS and then later Android, I was a solo developer having to build multiple apps. And so Flutter was one of the first crossplatform frameworks that I was able to use to to build apps really quickly. Um I uh worked in the mortgage industry. So I built mobile apps for uh loan officers and realators and then later worked at uh graphics programming and then um Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:08&lt;/strong&gt; · Thanks a lot. Yeah, that that sounds very exciting and I&apos;m super happy to hear about your journey. Um, so would you mind telling us currently like if you have any side project or what is the thing that excites you the most in the Flutter community with respect to your development or uh with respect to your project or something like that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:29&lt;/strong&gt; · Yeah, totally. So, um I really have enjoyed building with Dart. You know, it&apos;s a language that um is pretty extensible. You can compile to Wom machine code JavaScript and uh I feel like I&apos;ve uh enjoyed trying to do a bunch of weird things with it including getting web components working or working with uh JavaScript interop uh in different languages like see it with FFI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:53&lt;/strong&gt; · Um, but probably the thing I&apos;ve enjoyed uh most in the last couple years is I ported signals uh from Pact uh into uh Dart so I could use it with Flutter and it&apos;s been uh really fun to kind of bring some of the best of the JavaScript ecosystem into Flutter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:11&lt;/strong&gt; · That&apos;s that&apos;s great. Yeah. Uh I mean currently also the ecosystem is moving a bit towards AI. there is this AI buzzword everywhere and that&apos;s why with respect to this topic I would also like to deep dive a bit into the AI domain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:27&lt;/strong&gt; · So do you know which uh kind of models are used usually in Firebase Studio with respect to AI and and why have they selected these models?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:38&lt;/strong&gt; · Yeah. Um so uh Firebase Studio uses a variety of models including Gemini 2.5 Pro for the um app prototyper and by default Gemini 2.5 flash for uh the Gemini inside of Firebase. um those models are extremely capable and fast and by you know having a full Google stack you know working with the Google teams uh for Gemini also firebase we&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:07&lt;/strong&gt; · able to like really have a cohesive picture of like how we want to build the end toend experience you know we have the telemetry to see how the model&apos;s performing and how we can give that feedback and see ways that we can improve it of course there&apos;s prompting um but then also seeing how developers use it, right? Cuz uh you may build the perfect prompts and the perfect evals and then lots of people come in and use it a completely different way than you expected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:32&lt;/strong&gt; · And so uh it&apos;s been interesting to see how people have come to it and have been excited about, you know, bringing their own ideas to life through this natural language prompting. Um and then also inside of our regular chat, you know, we give you the ability to pick a custom model or even a fine-tuned one with a custom ID. Mhm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:54&lt;/strong&gt; · And so it&apos;s been interesting to see um people take advantage of that too. Uh we give you access to like the preview models if you want to like try something that&apos;s uh kind of bleeding edge and I think uh it&apos;s just been really cool to to experiment with that cuz every model has its own kind of unique quirks and stuff like that. Um but it&apos;s it&apos;s always a trade-off between speed and performance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:17&lt;/strong&gt; · And I think uh the if you come to Firebase Studio, you might be interested to see how fast you can build because we uh allow Gemini to read and write files and run terminal commands.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:30&lt;/strong&gt; · We have like a full context aware of your codebase. So it makes trying out these new features a lot easier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:36&lt;/strong&gt; · Yeah. Uh that&apos;s an interesting point that you said because even the student community is very much into flutter and the ecosystem and I think for them it&apos;s very important that they like get to try the product a bit and um wipe coding is also like one of the like trendy topics nowadays because even the people who are not so much into flutter development are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:59&lt;/strong&gt; · using these wipe coding platforms and could you just tell us how wipe coding uh how can one use wipe coding putting into Firebase Studio or how are they related to each other?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:10&lt;/strong&gt; · Yeah. Um I think uh it&apos;s been an interesting thing to watch where people come in with a bunch of different ideas um on how they&apos;re going to use AI to build um on an ex extreme. You&apos;ve seen people build SAS companies that are completely through natural language and trying to to build quickly in the open and and really fast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:30&lt;/strong&gt; · Um where I&apos;ve seen um particularly our vibe coding platform work really well is where you&apos;re using our templates uh for existing stacks like Flutter or React Native or even like we even have like .NET Laravel things like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:47&lt;/strong&gt; · Um, but then you can use uh Firebase Studio uh prompting with our app prototyping agent to build these secondary apps, you know, so things like your marketing page or uh implement new features or just try to get that first draft out there because it is so much easier to get something working and to see even if you&apos;re on the right path before you like kind of invest and start building.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:12&lt;/strong&gt; · So, um, one of the things that we&apos;ve done to make it really easy for developers is we&apos;re helping you build agent apps. Um, and so what I mean by that is like, uh, inside of the app prototyping agent, if you ask for a feature like an AI feature, it&apos;ll actually wire up the image generation or the multimodal input both to and from the backend. We use Genkit. Um, and so it makes building those types of apps a lot easier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:40&lt;/strong&gt; · So you can say like I want to have an app that gives me an inspirational quote and generate an image based on that quote and it can completely wire that up for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:49&lt;/strong&gt; · Yeah, that&apos;s that sounds amazing and I think as the as time progresses I think there will be more and more interest and with the documentations and uh a lot of resources I think it&apos;ll be more easier for people who are also not in this domain to use it more and to develop more apps I feel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:08&lt;/strong&gt; · Yeah, totally. So yeah and then like let&apos;s shift the focus from uh AI domain to bit of general like developer ecosystem. So as a GDG summit we have a lot of developer advocates here who are um kind of uh you know showcasing different products and with respect to that what do you think uh like why do you think Flutter has such a passionate community of users and why has it taken off uh so much in the past few years?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:38&lt;/strong&gt; · Yeah, I think um one of the things that&apos;s led to that is, you know, first of all, Flutter just has an amazing team and group of people that are leading it up. Uh the community is really welcoming and trying to encourage people to come in with little to no experience or completely with a huge background in native experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:59&lt;/strong&gt; · Um, I think where Flutter really shines is, uh, as a plug-in author, you can get something working on the various platforms and then the next person that comes in, all they have to do is write Dart. They don&apos;t have to know how is this going to work on desktop or web or mobile. They just start building with these abstract APIs. And so I think it really makes the the toil of development a lot easier to where you don&apos;t have to think about how will this render on the different platforms, how will this function work on different platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:31&lt;/strong&gt; · You just start thinking in features and you say like okay well I need to pick a file or I need to render a button, right? And so both of those abstract things work together to build like this really portable uh huge amount of code sharing uh crossplatform framework. And so I think even in regards to AI, Flutter has made it um really easy to build adapters and models. And so now you can build a chat interface or you can build an agentive uh application that you write once and you can run on all the platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:00&lt;/strong&gt; · And um even uh a recent uh development is notebook LM actually used uh Flutter for both their iOS and Android app as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:11&lt;/strong&gt; · Oh okay. I didn&apos;t know that. Yeah, I have used notebook LM for quite a different things. uh even translating a book into a podcast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:20&lt;/strong&gt; · So that was quite exciting for me and I I didn&apos;t know about this. Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:25&lt;/strong&gt; · Um yeah. So to uh like think a bit about the future of AI and the future of the Flutter ecosystem, where do you think uh this uh like AI with Flutter will be in the next few years? And um where do you see it going? like do you envision um like managers or AI taking the role of managers in in the Flutter ecosystem or even in general in AI?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:51&lt;/strong&gt; · So a bit about the future and what do you think the vision of uh Gemini with uh Flutter uh studio is?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:02&lt;/strong&gt; · Yeah. So, um I think that there is a lot of uh I feel like we&apos;re just now reaching the there&apos;s a lot left on the table. You know, there&apos;s a lot of potential still left to do. Um so, in particular, Flutter uh is really well positioned because when you want to build with dynamic UI or um interfaces that need to adapt to every platform, need to adapt to any type of input, all these things are really hard to do already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:31&lt;/strong&gt; · And so if Flutter takes that toil out and so you could say I want near native performance on all of these um application targets then that&apos;s a win. Um also Flutter has great support for like shaders and and other types of compute paradigms. So it makes building your custom renderer a lot easier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:49&lt;/strong&gt; · And so like we could see the role of like building agents or even portable run times, you know, if if you wanted to embed something into robotics because Flutter has its own renderer, you could literally port it just like you can on a Raspberry Pi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:06&lt;/strong&gt; · Um, and in Firebase Studio, you know, we want to make working with Flutter really great. And so I think that the world of working with Flutter is like, you know, trying to unlock the types of applications that people have been wanting to build. I feel like we&apos;ve seen a lot of especially for experienced developers a lot of explosive uh growth and learning you know especially when it comes to notebook LM and Gemini deep research and things like that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:32&lt;/strong&gt; · Yeah, definitely. I think yeah that is the like the way future and uh yeah that brings us to the concluding remarks for this uh podcast and I would like to thank you a lot for taking out the time to uh do this interview and um it was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:50&lt;/strong&gt; · very nice to chat with you regarding this and um yeah I hope that you also enjoyed equally and for our viewers um would you like to tell us about like where to connect with you and where can they follow your work or something like that and how can they keep in touch with the with the developer ecosystem?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:10&lt;/strong&gt; · Yeah, of course. Uh so, uh definitely check out Firebase Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:14&lt;/strong&gt; · Firebase.studio is our URL. Um I&apos;m also on all the platforms uh at Rodie Davis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:21&lt;/strong&gt; · Um you can also find me um uh anywhere uh in the Google groups and things like that. So yeah, I really uh thanks for having me on and uh hopefully uh you know there&apos;s more to talk about soon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:34&lt;/strong&gt; · Thank you so much and it was great to have you. Have a nice day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:37&lt;/strong&gt; · Awesome. Thanks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:39&lt;/strong&gt; · [Music]&lt;/p&gt;
</content:encoded></item><item><title>Interview with Rody Davis - Firebase</title><link>https://rodydavis.com/videos/interviews/interview-with-rody-davis-firebase/</link><guid isPermaLink="true">https://rodydavis.com/videos/interviews/interview-with-rody-davis-firebase/</guid><description>Firebase Developer Advocate Rody Davis explains how Firebase simplifies mobile and web application development by offering easy access to necessary features like databases, authentication, and secure AI integration.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Interview with Rody Davis - Firebase&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;D2q_y1GFAIo&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Rody Davis is a Developer Advocate for Firebase Studio at Google. Working closely with Flutter, Angular, Firebase, Go and Gemini in Google Cloud. In my time at Google I have also supported Material Design, Lit (previously Polymer) and Google Developers (Solutions). I have a background in systems administration, audio engineering, music and electrical engineering. I am very passionate about open source and creative applications of software. Currently I am working with teams to take advantage of AI in software development and integration into apps. My mission is to make the intersection of Google products work better together.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · And we are live here at Narala and we have Rody Davies with us. Hi Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:04&lt;/strong&gt; · Hi. How&apos;s it going?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:05&lt;/strong&gt; · Rody is developer relations uh engineer for Google at Firebase. So we&apos;re going to talk a little bit about Firebase and your experience here in the lab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:14&lt;/strong&gt; · So yeah, of course.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:16&lt;/strong&gt; · So in simple terms like what&apos;s Firebase and why does it help people to do things? Yeah, one of the cool things about Firebase is we are in Google Cloud trying to make it easier for you to connect to all sorts of APIs that can be traditionally hard if you&apos;re not an enterprise developer. So, if you&apos;re just wanting a mobile application and you don&apos;t want to have a backend, but we make it as easy as possible to start with a database authentication, sending out push notifications. We&apos;ve launched a lot of new features like um protection around if you have adversarial people trying to get into your app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:48&lt;/strong&gt; · We also have a way to connect with AI. So Firebase AI logic is a way to connect to AI securely without having to worry if you&apos;re going to send your API key to the client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:59&lt;/strong&gt; · I mean, and it&apos;s just for developers, Firebase or everybody can use the the tools and services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05&lt;/strong&gt; · So Firebase is traditionally targeted at developers, but with the launch of Firebase Studio, we really started to broaden that definition to just builders. We see a lot of people coming to Firebase Studio building an app for the first time or coming back to development after 101 15 years because now they can just give it an idea of what they&apos;ve been wanting to build maybe a game for their spouse or what&apos;s really cool is we see a lot of data scientists upload uh a lot of data and say like I just want to visualize this. I want to be able to solve this custom workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:36&lt;/strong&gt; · And so we&apos;ve seen some really cool use cases we didn&apos;t expect when we originally started building it. I was going to ask that like it&apos;s like you share internally like hey we saw this we saw that like this is amazing we didn&apos;t thought about it like it&apos;s usual.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:47&lt;/strong&gt; · Oh of course. Yeah we we really like being able to share. We even have on Fridays a customer demos. Well some of the best ones from the community will come and present to the team too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · Oh that&apos;s I mean awesome seeing other people do things with tools then it will bring innovation all over the place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · That&apos;s that&apos;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:03&lt;/strong&gt; · And do you have businesses using Firebase too? because like developers will be maybe the gate to the to the tool and then it grows into the companies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:10&lt;/strong&gt; · So with Firebase, we already have millions of businesses that are building on top of Firebase. It&apos;s already really popular. U with Firebase Studio, we&apos;ve already seen just in the short time since we launched since Cloud Next this year. We&apos;ve already seen multiple people say that they&apos;ve not only built an app with Firebase Studio, but they&apos;ve launched it to production and are already having customers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:29&lt;/strong&gt; · I mean that&apos;s that&apos;s really that really great. And now let&apos;s go a little bit into your personal experience with Firebase. We were talking a little bit off camera like what&apos;s your favorite use cases? I mean what&apos;s what do you love most to do with this and now with AI addon, right? Like what do you prefer to do with AI on Fire?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:45&lt;/strong&gt; · So AI obviously is a fastmoving pace and I think that there&apos;s some really cool things going on in terms of innovation, how we&apos;re using context effectively. One of the things that I have been really excited about with Google and Gemini is things like Gemini deep research notebook LM where you can take all the context and create your own knowledge database. But then I like to take it one step further and create some really cool custom workflows with Gemini CLI. So I can take my documents, use a research plan, and then create new context to send to the model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:15&lt;/strong&gt; · So I&apos;m basically compressing exactly what I need at any one time while being able to pull from all my resources. So like at a company you may have a PRD and a description of the designs and the different features that you want to implement. But so it&apos;s important to put that into a text format that the model can understand. But one thing that I think uh people may not realize is subtraction is actually more powerful than addition when it comes to AI. If you reduce a lot of the context and are only giving it the relevant bits, you&apos;re going to have a way better result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:46&lt;/strong&gt; · Even with Gemini having a 1 million context window, we found that effectively if you&apos;re using about 20% um you can get a lot more performance out of that that context window, it&apos;s really good for searching long PDFs and being able to do some really cool context, but to get that high performance, you can also take advantage of the implicit caching and things like that too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:06&lt;/strong&gt; · That&apos;s great. And and if we go just to the new features that we have on Firebase, what would be your maybe top one or top two new features in the last time? So, one of the cool things that we launched really recently is Firebase AI logic, which allows you to proxy all your requests to our back-end servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:23&lt;/strong&gt; · Um, traditionally, you&apos;ve we&apos;ve you&apos;ve been able to like build with AI using a lot of the common SDKs, but people are always worried about sending their API key to the client. Well, Firebase Logic will actually proxy it, so you never have to worry about that. But you can still run inference on Google Cloud through Vertex or the free tier in Geminina API. So we&apos;ve seen some really cool use cases. Not only that, we partner with Chrome. So now on certain devices, it&apos;ll even run the ondevice model of Gemma or Gemini Nano actually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:53&lt;/strong&gt; · So you can completely run offline and then fall back to that cloud hosted model. We&apos;re trying to bring that to other mobile devices too, but it has been such a cool use case that like you just write your API and then your your API costs go down if they they have a supported model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:08&lt;/strong&gt; · That&apos;s awesome. Yeah, a lot of things going on and it&apos;s really exciting to be right now with AI and everything going back to the device or the browser. So just as a final question like what would you recommend for someone that wants to begin in Firebase like do they have communities resources online? Just a brief comment on that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:24&lt;/strong&gt; · Yeah, of course. So Firebase is a massive community with a lot of people all over the world. Um if you&apos;re in an area that we um have supported which is almost all places you can look for what are called Google developer experts and we have communities here that you can get started with meetups and they&apos;ll connect you with all kind of uh resources. We also work with those people so we can uh make sure that we have content that&apos;s scalable and then of course uh there&apos;s Googlers you can find the events we have a lot of content online code labs pathways you name it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:54&lt;/strong&gt; · That&apos;s awesome. Thank you for the time here, Rory.&lt;/p&gt;
</content:encoded></item><item><title>Speaker Interview - Rody Davis, David Khourshid, and João Luiz S Kestering</title><link>https://rodydavis.com/videos/interviews/speaker-interview-rody-davis-david-khourshid-and-joo-luiz-s-kestering/</link><guid isPermaLink="true">https://rodydavis.com/videos/interviews/speaker-interview-rody-davis-david-khourshid-and-joo-luiz-s-kestering/</guid><description>ThunderNerds hosts a speaker interview with Rody Davis, David Khourshid, and João Luiz S. Kestering discussing mobile, front-end, and general technology development at Devfest Florida.</description><pubDate>Wed, 20 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Speaker Interview - Rody Davis, David Khourshid, and João Luiz S Kestering&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;RuVyxtZZbhY&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;The ThunderNerds interviewing our speakers at Devfest Florida.&lt;/p&gt;
&lt;h3&gt;Rody Davis&lt;/h3&gt;
&lt;p&gt;Full stack mobile developer from Alabama and currently in Tampa for the last 7 years. I have created over 12 apps with on iOS and Android released for production. I love VR, Game Development, Music production and graphic design. I have been using Flutter since beta 2. I contribute a lot to open source and love showing others how tech can make our lives better and how great UX can make us love the products we use.&lt;/p&gt;
&lt;h3&gt;David Khourshid&lt;/h3&gt;
&lt;p&gt;David Khourshid is a software engineer for Microsoft, a tech author, and speaker. Also a fervent open-source contributor, he is passionate about statecharts and software modeling, reactive animations, innovative user interfaces, and cutting-edge front-end technologies. When not behind a computer keyboard, he’s behind a piano keyboard or traveling.&lt;/p&gt;
&lt;h3&gt;João Luiz S. Kestering&lt;/h3&gt;
&lt;p&gt;I&apos;m software engineer on Modus Create with post-degree in Software Architecture with focus on frontend applications using Angular and React. Now started a new adventure using Flutter. On my free time, I like to watch football, read and play video games.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · welcome to thunder nerds I&apos;m Brian Henson I&apos;m not a project Philip bond bison thank you for consuming the thunder nerds conversation with a lot of the people the technology I love what they do and do tech good and we&apos;re here at deaf Fest 2019 where we a pariah we&apos;re in our land oh it&apos;s okay it&apos;s a nice job we&apos;re here with a lot of the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · percenter speakers here why don&apos;t we just go around the room what&apos;s your name sir Rory Davis um yeah I&apos;ll stock up mobile developer with our excellent this guy looks really yeah David of course she doesn&apos;t in this ad Stephen Kate that&apos;s right and to Elise from Vizio and yeah software developer much great excellent great thanks you all for joining us I know we have a lot of people on here we&apos;ll try to what exactly do you do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04&lt;/strong&gt; · watch what&apos;s your role yeah so I took over the mobile department up the company that I work at we built a CRM with flutter and so yeah it&apos;s a business-to-business application so I have a ton of users about California and all around the world so yeah it&apos;s been pretty cool took a product that didn&apos;t exist at all because when I joined they didn&apos;t have a mobile app and so they have a really old web application but completely rebuilt it and we&apos;re gonna make a flutter web and yes actually running like David so I work with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39&lt;/strong&gt; · Microsoft on the CSE team which stands for consumer software engineer we create innovative solutions for companies using Azure announcer services such as just like the machine learning there&apos;s so much that we do like I&apos;m not even doing JavaScript right now I&apos;m doing sorry his right to bring some of that to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:13&lt;/strong&gt; · the key framers well I&apos;m work out front an engineer from the most great but I&apos;m Merkel okay to idea so there&apos;s a large banks that using sixty and I&apos;m from birthday Oh excellent okay great and everyone I assume has already spoken today alright so we know we round and talk about all your all your mean jokes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:43&lt;/strong&gt; · to did a duo talk on moving an angular application to flutter and of course I&apos;d like six months so we took a full-fledged six months yeah yeah yeah and the last version we did like in a month but basically we built a full fledge so we amp stories as a new format&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:02&lt;/strong&gt; · coming out that&apos;s getting supported by Google Google search right that&apos;s off we can create like snapchat stories for the web basically and we built a full editor that you can do without coding you can build all these stories and then just share a link and then have a hosted an HTML website so I mean you&apos;re ready to build a hot drama yeah why why that tell&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:24&lt;/strong&gt; · me like what what made you interested in doing that right now there&apos;s there&apos;s only one other editor so this space is wide open for people that take on so we found that not only could we make a splash with the you know just being another editor on the market but also being fluttered we have a huge community so being one of the first really big web applications though imagine like we are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:50&lt;/strong&gt; · changing our view from web and how we consume the the content for right I mean before I really using like this crew and now is so you see the stories and it&apos;s terrible mmm not more scrolling so this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:06&lt;/strong&gt; · is the difference in our editor makes it faster using amp in the mobile applications because you can create a civil rate and share the link within a matter of like two minutes yeah so hopefully hosted everything so you can take a picture and put some taxi and sure on the web on the wrong side of interest yeah so who would you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:29&lt;/strong&gt; · say is the best example as like somebody that this will be a benefit yeah so there&apos;s already people using this kind of technology like CNN and like you know these Mashable these huge marketing firms like they have people that have this content we even created a CI SDK&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:47&lt;/strong&gt; · that can use it we can take a JSON schema and export an HTML to so we will that as well but this whole process is we&apos;re connecting the marketing people being able to create websites with an without having to go into coding and stuff like that yes are you all hosting yeah we use everything Google we use cloud run for the SDK we use a firebase hosting for the website we have Google Cloud storage to managing all the actual things&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:20&lt;/strong&gt; · David what about you talk so my talk was on mind-reading with the depth of an intelligent user interfaces which is [Laughter] user interfaces in funny not even though I work at Microsoft I ended up talking a lot about Google technology they created&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:42&lt;/strong&gt; · something called guess jeaious which was written by Sam Ciccone works at Google which predicts which peach are going to visit next in pre-loaded so you have this really fast experience if you click the link in the page to show so it&apos;s like wow my internet go a lot faster&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:59&lt;/strong&gt; · adaptive books for react which allow you to basically check like okay this device is like an edge device it&apos;s like one that might not have enough data so we want to show them a limited view rather than a full view but I went further and I was like using model driven development so with state machines and state charts we can easily create these adaptive user interfaces that just predicts anything the user doesn&apos;t be app in real time Wow yeah I imagine that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:26&lt;/strong&gt; · could even translate to web like I know worse place I could see where you you&apos;re browsing around the website where might they go next and right instead of like having to do like oh I always do this step this I visit this that&apos;s like it predicts like yeah you probably want that so we&apos;re just gonna make it streamline perfect yeah where how&apos;s it working machine learning reinforcement learning that&apos;s cool so what kind of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:56&lt;/strong&gt; · points do you want people to get away from that from your talk well the main point is just this whole idea of model driven developments which is not something we talk about nowadays because startups are all about move fast and break things right yeah it&apos;s like you have a problem you throw you throw code&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:14&lt;/strong&gt; · and a lot of money at it and you&apos;re like hopefully the problem will solve it enough right and so model driven development is taking a step back and saying before you do anything take pencil and paper start drawing out your solution your box is Union your arrows and then once you have a very clear picture of what your solutions going to look like then start coding and so that has a lot of benefits even there&apos;s more upfront cost it was a bit best so what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:56&lt;/strong&gt; · about you what&apos;s one thing don&apos;t be afraid of flutter web we were kind of one of the pioneers with you know building plugins and stuff for the web for flutter angulardart I don&apos;t know if you guys are familiar but that used to be that was a thing for a long time and people are starting to transition to flutter web because you can use all existing plugins already regularly and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:21&lt;/strong&gt; · then addition to mobile you get to just have this awesome experience on the web so you can build the branding exactly how you want so instead of like relying on HTML and CSS you&apos;re just saying declaring your UI on the web which is and then oh I want a desktop application then you just export to a desktop like you don&apos;t have to do anything else yeah that&apos;s so wonderful that saves so much time now what&apos;s the what&apos;s the negative of flourish there&apos;s so many positives what&apos;s like the big negative is there a big negative if you&apos;re only using one platform yeah sometimes it&apos;s better to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:54&lt;/strong&gt; · just do it on that platform like if you&apos;re only never gonna do Apple yeah then just do it with Swift UI if you&apos;re gonna ever have another extension of your application well to play devil&apos;s advocate what if I put out a application that that&apos;s just for Apple and it&apos;s super successful and I go oh let me try that over here I already have code so yeah one of the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:18&lt;/strong&gt; · cool things about floaters you can reuse your existing Swift and Kotlin I mean abductor C code as well as you know connect your application you can add a flutter view without having to break your app you can do it end opted actually I had a really successful iOS application I but like the most popular pitch buy from the world and because of that everybody was asking for an Android application I rebuilt the same application in a matter of like three weeks that it took me eight months to build on iOS wow that&apos;s amazing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:46&lt;/strong&gt; · mmm that&apos;s just extremely fast to work with yeah and you you took six months to build a flutter how long did it take to build your presentation like how much time presentation my present today so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:16&lt;/strong&gt; · it&apos;s actually built on many presentations that I&apos;ve given like just this year I wait too many conferences basically every single time I give a talk my like my talk involves and all add slides change slides based on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:37&lt;/strong&gt; · previous information right that&apos;s a ghost with a lot talks in the last speakers I talked to is that what takes the months is the research looking up developing the ideas and then once they&apos;re like all right yes now I have a very clear way of how me and talk about this then it&apos;s like well the conference is in two days it&apos;s a lot of work that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:57&lt;/strong&gt; · goes into it it&apos;s funny I talked to I&apos;ve seen some things on Twitter where people be like oh I don&apos;t want to go see a conference with you know why does speakers talk about just doing it this morning it&apos;s so rude to the conference attendees and it&apos;s like you&apos;re not really doing it this morning education your years of experience so it&apos;s a lot more than that it doesn&apos;t matter if it took you half an hour to put it into a PowerPoint or whatever it is it still took you months&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:33&lt;/strong&gt; · it&apos;s not so much about what you are presenting it&apos;s about what the attendee is going to learn if you put in a half hour it doesn&apos;t matter if I got something out of that that was truly inspiring to you know oh I wanted to learn flutter or you know that there&apos;s the back workshops these are these are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:03&lt;/strong&gt; · presentations yeah you go there to learn a little bit about something that might inspire you to go you know what I&apos;m gonna go home movie trailer it&apos;s a movie trailer exactly you get you excited about it you learn a little bit enough about oh you know what I didn&apos;t think about it that way I&apos;m gonna take that and bring them with me and how do you all how do you pare it down so it&apos;s not&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:28&lt;/strong&gt; · like a you know two-hour presentation well it&apos;s really just like a snapshot of where you&apos;re at like so you know when you go down like I try to procrastinate as long as possible because when I do I can build it extremely fast but you know the longer I wait the more half the time to format the ideas so you know I may&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:46&lt;/strong&gt; · have not have been writing the presentation but I&apos;ve been thinking about things I want to say so then it&apos;s just like a matter of okay how much time do I have you know just trying to shoot like maybe a slide a minute and then just yeah I just tried to shoot for your a lot of times though it makes sense so everybody since you&apos;re in the same raunchy yeah yeah so what what I do is like you once&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:04&lt;/strong&gt; · appear down your presentation to one core idea for example for me it&apos;s how to develop these adaptive user interfaces using molitor&apos;s and development and so I&apos;m like alright so there&apos;s like this precursory knowledge of what is MA driven Spelman was that the user interfaces and then literally everything else like when someone shows coming on the screen I don&apos;t expect anyone to read it I&apos;m just like here&apos;s where you could find this here&apos;s more resources yeah you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:33&lt;/strong&gt; · know it&apos;s just to get exposure I always said that Danya going to conferences for a conference at Sandy is for two things number one for networking of course the biggest values and number two is for learning what to learn not learning yes learning wasn&apos;t learning yes yeah what&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:03&lt;/strong&gt; · we&apos;re actually using a darker we&apos;re one of like the you know cloud room yeah we&apos;re actually we&apos;re actually able to use our existing like the flutter application for the business logic through a REST API like it&apos;s a whole crazy thing so instead of having a client a server application using the same application with different endpoints which is so we can reuse all existing business logic former models but expose it in a different aspect so it&apos;s because like a lot of really cool things that we can use to save how much we&apos;re doing and being able to have you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:33&lt;/strong&gt; · know different what talk like really like resonated with you so earlier today&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:01&lt;/strong&gt; · I actually saw Lee works talk it was a provocative talk and he&apos;s actually a great speaker I think this was first conference he gave but he talked about you don&apos;t deserve senior developers and so it was oh you have enough needs where it&apos;s like we&apos;re all hiring for all these senior developers but really the belly comes from your junior developers plus in Central Florida no one can afford senior developers and yes no like quick trying get some juniors and we&apos;re by hiring&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:27&lt;/strong&gt; · junior developers you get all this value like they&apos;re able to tell you what are the pain points in your code base and you&apos;re able to basically foster them into these senior developers that have very specific domain knowledge which is more valuable than just an easy new developer you pick off the street oh yeah okay which most like the one we had rekka for us about peri-urban development because we actually I&apos;ve never heard that before ever thought of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:07&lt;/strong&gt; · maybe a junior senior depth getting together sound awesome I start learning angular yeah did you get one of those&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:38&lt;/strong&gt; · like certificates we actually he&apos;s in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:55&lt;/strong&gt; · Brazil and I&apos;m in Tampa so yeah we&apos;ve been yeah that&apos;d be like doing like a like a podcast like a live stream for like I don&apos;t know how long months and months and months and then never meeting the person&apos;s yeah don&apos;t like just ran if they have a meeting them in Texas it was around because I was doing a react stream workshop at the time there was artifact comp going on too and that was actually the first conference that he&apos;s been to so he&apos;s like I&apos;m not them like yeah they&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:32&lt;/strong&gt; · suck means the conference they want to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:47&lt;/strong&gt; · be on your chest yeah I actually found that kind of interesting because Dave a lot of people in history probably look up to Dave and Chris a great deal and yet for him so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:09&lt;/strong&gt; · what was your if I could ask go around the room what was your favorite part about the conference today well this was actually the first conference eyes okay yeah and I&apos;m actually moving outside Silicon Valley so I hope to speak out a lot more so as long as to see everyone here and meet the what about you did I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:46&lt;/strong&gt; · love the friendliness of this conference like just so many people to talk to in because it&apos;s three different areas you have this or the you know which is like completely new it&apos;s like yeah I love that track in in Orlando what I really want to foster because I&apos;m part B lauric Orlando devs you know many things we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:05&lt;/strong&gt; · want fosters just bringing this whole entrepreneur business side and developer side together because they seem very separate and we wants to you know just make them work together and so that&apos;s what they&apos;re trying to at this conference to which is why we have the startup stage the mobile states the web stage so it&apos;s really interesting just you know seeing all those different perspectives yeah yeah for me my first international talk so oh really okay my first talk in its international and I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:41&lt;/strong&gt; · meet some great people that I was like one year you know for me that&apos;s so cool so what was the biggest surprise when you met each other like so I&apos;ve worked my Dollard in remote and like everyone thinks I&apos;m super tall I don&apos;t know I don&apos;t know why maybe I have the camera like yeah I&apos;m always shocked that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:15&lt;/strong&gt; · [Music] everybody looks the same for me last&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:36&lt;/strong&gt; · like six months we&apos;ve been talking three days a week awesome so how what&apos;s the best way people could we&apos;ll go around room what&apos;s the best way people could learn more about you I&apos;m extremely active on github so you can follow me at Apple educate and I&apos;m also on Twitter at Brody Davis excellent and store team yes and they answer David so I&apos;m on Twitter github&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:02&lt;/strong&gt; · everywhere as ATS David Kay piano because I play the piano is school piano but also if you type in the word steam machine literally creating imaginative&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:28&lt;/strong&gt; · in there user interfaces from like with HTML CSS JavaScript soon another guest coming soon so that was a really fun and but yeah we do hope to have some more guests on the show - all right awesome from you Jessica SK so it&apos;s three there&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:56&lt;/strong&gt; · and github and I&apos;m stalking awesome great yeah and we&apos;ll have links to everything in the show notes well thanks everyone for joining us really appreciate it thanks everybody for watching and uh appreciate you spending some time with us at that fest take care everybody&lt;/p&gt;
</content:encoded></item><item><title>5 things you can do to prepare your app for large screens | Session</title><link>https://rodydavis.com/videos/talks/5-things-you-can-do-to-prepare-your-app-for-large-screens-session/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/5-things-you-can-do-to-prepare-your-app-for-large-screens-session/</guid><description>This session outlines five practical strategies, including using column grids, that developers and designers can implement to adapt existing apps for large screens using Material Design, even without a complete redesign.</description><pubDate>Wed, 19 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;5 things you can do to prepare your app for large screens | Session&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;UNDZn9GKJGo&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;In this Session, you learn how Material Design can help you get your app ready for large devices when time and resources are constrained. From prioritizing design characteristics to implementation details, this Session will give developers and designers a picture of what’s possible without undergoing a complete redesign or a comprehensive investment in responsive design.&lt;/p&gt;
&lt;p&gt;Resources:
Material Design for Large Screens → https://goo.gle/3dXWdR5&lt;/p&gt;
&lt;p&gt;Speakers: Liam Spradlin, Rody Davis&lt;/p&gt;
&lt;p&gt;Watch more:
Material Design at Google I/O 2021 Playlist → https://goo.gle/io21-MaterialDesign
All Google I/O 2021 Technical Sessions → https://goo.gle/io21-technicalsessions
All Google I/O 2021 Sessions → https://goo.gle/io21-allsessions&lt;/p&gt;
&lt;p&gt;Subscribe to Material Design → https://goo.gle/MaterialDesign-YouTube&lt;/p&gt;
&lt;p&gt;#GoogleIO #Design #Android&lt;/p&gt;
&lt;p&gt;product: Material Design - General; event: Google I/O 2021; fullname: Liam Spradlin, Rody Davis; re_ty: Premiere;&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · （欢快的音乐） 大家好，我是Rody，是 Material Design的开发者倡导人 我是Liam，是Material Design的设计师和设计倡导人 今天，我们将探讨 为大屏设备准备应用时可以做的五件事情 我们知道许多开发者 会构建可以在多种设备上运行的应用 而借助类似于Foldables的新平台 对适用于各种尺寸的 自适应应用的需求从未如此强烈 我们最近在material.io上 发布了新的设计指南和更新 这个讲座将帮助您总结一些 您现在就可以开始做的重要事情&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:33&lt;/strong&gt; · 且不需要完全重写您的应用 首先，我们来讨论一种应用布局的方法 这种方法可以使布局更具结构性 能够合理地适应屏幕 尺寸和列网格不同的各种设备 为何要使用列呢？ 如果您是为Web而构建 那您可能已经对这个概念及其重要性很熟悉了 但出于某些原因 列对于将界面设计概念化非常有用&lt;/p&gt;
&lt;h3&gt;Column Grid&lt;/h3&gt;
&lt;h3&gt;Column Grids Provide Convenient Structure&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · 首先，列网格提供了方便的结构 列布局已在打印布局和图形设计中使用了数十年 用以创建打印工件所需的结构 使打印材料符合人体工学并具有最大的可读性 印刷商使用并优化这些布局，帮助他们的用户 也就是读者，最大程度地利用Material 我们在界面设计中也可以将它们用于相同的目的 现在，许多应用都将屏幕视为一个大画布或一个列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · 在水平和垂直方向上相对于 彼此的位置来绘制元素 也可能会在边缘留有边距 这种方法对于较小的上下文可能管用 但如果上下文较大，问题就会变得非常明显 将列网格应用于布局可以使大屏设备的体验 更加周到细致且显得井井有条 您会感到设备和上下文有机地结合在一起&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · 其次，列让您能够将屏幕划分为 包含相关信息和操作的多个清晰区域 从而改进信息的层次结构 在本例中，您可以看到三个清晰的区域 每个区域都将用户的视线引导到屏幕上的 主要信息片段或组以及区域的预期使用顺序 最后，也是最重要的一点 列网格通过提供一种合理的方式来思考 内容在屏幕尺寸变大或 变小时应如何进行自动重排 从而适应所有屏幕尺寸 在本例中，我们可以看到三个 主要区域中的每个区域都有四个或四个以上的列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:14&lt;/strong&gt; · 因此，如果屏幕缩小 内容将自动重排，从而保持相同的层次结构 但会以更符合人体工学的方式填充缩小的屏幕 这就是在没有叠加的情况下，最终成品的样子 接下来，Rody将告诉我们如何实现这些概念 在本次课程中，我们将重点 介绍如何为Android实现列布局&lt;/p&gt;
&lt;h3&gt;Implementing a Column Layout for Android&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:31&lt;/strong&gt; · 根据您要构建的平台，具体内容可能会有所差异 采用基于列的布局进行构建时 将根据屏幕尺寸来定义运行时的列数 边距也将根据断点的不同而发生变化 并具有可变值和固定值 正文的最大尺寸也会根据屏幕尺寸而变化 可以按平台进行调整 若要在Android上实现基于 列的布局，您可以使用约束布局&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:54&lt;/strong&gt; · 您只需几个权重和链即可轻松创建 复杂的视图和嵌套布局 如果您有想要重复使用的部分 可以将它们提取为Fragment 若要构建8个列的布局 首先需要添加基本约束布局 并且布局的宽度和高度应与父级约束相匹配 现在，我们可以连续添加第一项 确保为它提供一个DP值为零的布局&lt;/p&gt;
&lt;h3&gt;8-Column Layout&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:13&lt;/strong&gt; · 使它与约束匹配 在左侧，我们将它约束为父级 在右侧，我们将它约束为链中的下一个子级 通过这种方式在左侧和右侧添加约束 我们可以构建一条水平链 添加权重后，该链将可按预期弯曲 现在，可以使用相同的布局宽度添加第二个子级 这次，我们在左侧约束为上一个子级 在右侧约束为父级 这样，随着父级尺寸的增加，水平链和&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:37&lt;/strong&gt; · 所有指定的权重也将随之增加 我们已经有带两个子级的约束布局 现在可以指定权重了 由于我们要在这个布局上构建8列网格 所有子级的权重加起来需要等于8 我们希望第一个子级跨越两列 于是将它的水平权重设为2 对于第二个子级，由于需要加起来等于8 我们给它分配权重6，这意味着它将跨越6列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:00&lt;/strong&gt; · 我们要讨论的第二个重点是在您的界面中 创建一篇可以缩放的文章 为此，我们需要采用一种可靠的方法 来定义布局中的各个常见部分 我们在Material中考虑这个问题的一种 方法是使用所谓的布局容器来构造缩放布局 在制作大屏设备指南时，我们考察了&lt;/p&gt;
&lt;h3&gt;Layout Containers&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:19&lt;/strong&gt; · 应用整体布局中最常见的元素，并提出了 一种实现一致自适应行为的思考方式 我们将屏幕分为三个主要容器 第一个是包含应用中大部分内容的正文容器 在我们的Fortnightly 示例中，就是文章内容 这个容器基于之前的列网格 以及断点表中定义的灵活边距和宽度 其次，我们有一个导航容器 这个容器由一些导航组件组成&lt;/p&gt;
&lt;h3&gt;Body Container&lt;/h3&gt;
&lt;h3&gt;Navigation Container&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:45&lt;/strong&gt; · 这些组件可使用户在目标之间移动并触发关键操作 展开时，始终保持256 dps 折叠时，则始终保持72 Dips 这个容器会被放置在缩减正文容器 尺寸的屏幕的边缘 在我们的例子中，您可以看到文章的正文 变得略窄了一些，以适应完全展开的导航容器 最后是应用栏容器 顾名思义，这个容器通常包含一个 应用栏，让用户可以访问一些关键操作 例如打开和关闭导航容器、搜索或移动至支持目标&lt;/p&gt;
&lt;h3&gt;App Bar Container&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:14&lt;/strong&gt; · 在我们的示例中是这个样子，感觉就像是一个 更完整和全面的布局 屏幕上的三个容器和区域均清晰可见 我们可以合理地预测它们在其他尺寸设备中的样子 就像我们刚刚了解的那样，使用基于列的网格 有助于将屏幕划分为清晰区域 从而创建清晰的信息层次结构 但在这些区域内，重要的是要创建令人信服的内容 我们将讨论两种相关技术。&lt;/p&gt;
&lt;h3&gt;Visual Grouping&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:37&lt;/strong&gt; · 首先是视觉分组 这是一个总体布局概念 旨在通过使用空白和版式 将具有相似内容功能和含义的 相关元素组合到布局中 您现在可能已经在您的应用中实现视觉分组 在实现视觉组时，可以在网格和列表周围环绕空白 它可以是组合了Material卡 或图块的列表视图或网格视图 若要构建更复杂的布局，您可以 嵌套列表和网格，对相关内容进行分组 在Android上，您可以使用 回收器视图将相关内容分组为列表或网格&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:10&lt;/strong&gt; · 为相应的断点设置正确的容器内距离非常重要 当在较大的布局上嵌套时 可以使用空白进一步与其他内容保持距离 第二种技术是包含，这是一种更为明显的方法 它依靠清晰的边界 比如高程或可见的分隔线，来达到相同的效果 Material提供了许多 可实现包含的预制组件，例如卡片 在Android上，您可以通过在 Material卡片视图上包含内容来实现包含&lt;/p&gt;
&lt;h3&gt;Containment&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;6:36&lt;/strong&gt; · 高程或Outline是可选的 但这有助于以明确的方式 包含子级 您也可以使用Material列表图块 来实现包含 在Android上，您可以使用前导图标图像 视图、标题和字幕来创建约束布局 接下来，我们讨论适合这个工作的组件 -Material提供了可在您的 应用中使用的组件的目录 借助我们的大屏设备指南 我们更新了每篇文章的内容 以便更好地说明 它们对于不同设备的适应行为 视觉呈现是为Material 组件描述的第一种适应类型&lt;/p&gt;
&lt;h3&gt;The Right Components for the Job&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:06&lt;/strong&gt; · 它指的是元素在屏幕上的缩放比例和位置的更改 以及彼此之间的关系 例如，移动设备上的文本列表可以调整其边距 垂直间距或密度，以便更好地适应 平板电脑等大屏设备 这种类型的适应非常重要 因为在切换不同屏幕尺寸的 设备时，它可以保持连续性视觉奇偶校验 从而让用户也可以保持一种应用的心理模型 实现它的一种方式就是尺寸约束 大多数Material组件 都具有最小和最大尺寸 当布局从移动设备扩展到大屏设备时&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:38&lt;/strong&gt; · 这些值会在视觉呈现时 连续变化，同时保持可用性 在本例中，按钮会忽略尺寸约束，并在小屏设备上 被压缩，从而消除内置的容器内距离 而这个距离主要用于使触摸 目标始终保持足够大以便按压 对于快餐栏，小屏设备和 大屏设备都有最小和最大尺寸 在小屏设备上，快餐栏 可以向上扩展以适应其消息的传递 在大屏设备上 最好只占用一行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:01&lt;/strong&gt; · 快餐栏可以水平扩展 对话框可以水平扩展以承载其内容，也可以使用 更多的屏幕空间，直到达到最大宽度为止 在最大宽度下，对话框可以垂直扩展&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:16&lt;/strong&gt; · 若内容导致对话框大量扩展 可以考虑使用其他呈现方法 顺便一提，像这样的扩展对话框可以很好地 代替全屏对话框，以实现移动体验 作为开发者，您可以针对您的布局 根据组件尺寸的约束范围，充分利用 组件使用固定尺寸或可变尺寸的能力&lt;/p&gt;
&lt;h3&gt;Fluid Dimensions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;8:36&lt;/strong&gt; · 例如，固定尺寸是指 无论使用哪种设备 按钮都将保持相同的尺寸 而可变尺寸使按钮可以在其约束范围内 继续适应布局的其余部分 考虑内部组成或组件内部元素 在适应时的行为也非常重要 虽然很多组件都是内置的 但如果您有自定义组件或更复杂的布局 还是值得讨论 在前面的对话框示例中，请注意&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:03&lt;/strong&gt; · 随着对话框扩展标题、正文和按钮之类的内部元素 如何保持它们与组件边缘的关系 标题和正文文本彼此之间 也会保持一致的位置 这种内部组成对于用户在每个组件适应时 保持其心理模型至关重要 应用栏则是另一个范例，但它增加了一点复杂性 菜单和标题通常是起始对齐 操作图标等内容是末端对齐 而搜索栏则与边缘或中心对齐 在Fortnightly的这个 变体中，我们可以看到&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:33&lt;/strong&gt; · 徽标和菜单按钮与起始边缘对齐 帐号选择程序位于边缘的尾部 而搜索栏则与承载文章文本的 正文容器的列对齐 在这个配置中 Fortnightly可以水平缩放 同时使所有这些元素之间保持一致的关系 组件切换是另一种较为罕见的适应类型 具有等效功能的组件实际上可以相互切换&lt;/p&gt;
&lt;h3&gt;Component Switching&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;9:54&lt;/strong&gt; · 以满足不同设备的人体工学要求 例如，底部导航栏 在平板电脑上可能会切换为轨道式导航栏 而在大屏设备上则是抽屉式导航栏 切换组件时请务必小心 应确保要切换的组件具有等效的功能 最重要的是，组件切换应服务于用户的 功能和人体工学目的 我们要考虑的第四个要点是 您的应用如何保持良好的连续性 您构建的内容不再仅针对运行时 无法改变的一种外形尺寸的设备 您的应用将随时根据屏幕的尺寸和方向进行调整&lt;/p&gt;
&lt;h3&gt;How Your App Maintains Great Continuity&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;10:26&lt;/strong&gt; · 为确保为用户带来出色的 体验，请务必保存滚动位置 所选项目和正确的导航层次结构 对于Android来说，请务必保存即时状态 以便界面可以在不同方向 保持不变，并可根据屏幕大小进行调整 为确保用户获得更好的体验 请将所选项目以任何形式保存在REA中 使用自适应布局，每次根据屏幕尺寸 进行调整时，都会重新创建界面状态&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:48&lt;/strong&gt; · 我们要讲的第五点 也是最后一点就是新的交互模型 无论您的应用是在电视 笔记本电脑、手机、汽车还是仅在语音模式下运行 您都可以采取措施为所有用户提供出色的体验 当您的应用在大屏设备上 运行时，您可以考虑新的状态 例如悬停状态和焦点状态 使用大屏设备时，应使用滚轮来控制滚动操作 而不是使用拖动手势 使用移动设备时，可以使用双指张合及缩放的手势 但在没有触摸屏的大屏设备上&lt;/p&gt;
&lt;h3&gt;The New Interaction Models&lt;/h3&gt;
&lt;h3&gt;Scrolling&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;11:20&lt;/strong&gt; · 应使用触控板或鼠标滚动事件来进行控制 在适应大屏设备时，您需要考虑新的输入类型 使用平板电脑或笔记本电脑时 用户使用的可能是硬件键盘，而不是 内置的软键盘。您还应考虑通过定向输入 例如电视，来进行控制应用的远程游戏手柄输入 以及语音控制或其他出于 无障碍功能考虑的被动输入模式 最后，在无法使用手势的桌面设备上运行时 显示上下文菜单与底部 工作表以及其他右键单击操作 我们已经介绍相关基础知识 请在material.io上查看有关布局和&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:53&lt;/strong&gt; · 组件的最新指南 - 并在Material博客上阅读本次讲座 随附的相关博文 如果您有任何疑问 请随时通过Twitter与我们联系 并关注YouTube上的更多更新和教学视频 感谢您的观看&lt;/p&gt;
</content:encoded></item><item><title>Design at scale with Web Components (and ducks)</title><link>https://rodydavis.com/videos/talks/design-at-scale-with-web-components-and-ducks/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/design-at-scale-with-web-components-and-ducks/</guid><description>Web Components offer a scalable solution for design systems, enabling teams to create and deploy consistent, on-brand user experiences across any modern web framework.</description><pubDate>Tue, 22 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Design at scale with Web Components (and ducks)&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;DBcz_bGcHgk&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;You have a beautiful design system that delivers consistent, on-brand user experiences! Now, how do your designers and engineers provide your design to teams using any framework from React, to Vue, to Angular, and more?&lt;/p&gt;
&lt;p&gt;The Material Design team shares how Web Components are a great choice to empower design systems at Google scale.&lt;/p&gt;
&lt;p&gt;Design once. Build once. Use everywhere.&lt;/p&gt;
&lt;p&gt;Resources:
Lion Web Components → https://goo.gle/363MUey
Prefers-color-scheme → https://goo.gle/2UXBp1F
Spectrum Web Components → https://goo.gle/3pZXomU
Quack→ https://goo.gle/quack
Github material-components-web-components → https://goo.gle/mwc&lt;/p&gt;
&lt;p&gt;Speakers: Liz Mitchell, Rody Davis&lt;/p&gt;
&lt;p&gt;Watch all Chrome Developer Summit sessions here → https://goo.gle/cds20-sessions
Subscribe to Google Chrome Developers here → https://goo.gle/ChromeDevs&lt;/p&gt;
&lt;p&gt;#chromedevsummit #chrome #webdesign&lt;/p&gt;
&lt;p&gt;event: Chrome Dev Summit 2020; re_ty: Publish; product: Chrome - General; fullname: Rody Davis;&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · (upbeat music) Hi, welcome to Design at scale with Web Components and maybe a few ducks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:12&lt;/strong&gt; · My name is Liz, I&apos;m a Software Engineer on the Material Design Team at Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:17&lt;/strong&gt; · Here on the Material Team we believe in a simple philosophy to create design systems at scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:23&lt;/strong&gt; · Design once, build once, use everywhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · If you don&apos;t know us already you may be wondering who or what is Material?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:35&lt;/strong&gt; · Luckily, I&apos;ve got a great partner with me here who knows a lot about that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:40&lt;/strong&gt; · Take it away, Rody Thanks, Liz.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · My name&apos;s Rody and I&apos;m also on the Material Design Team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:45&lt;/strong&gt; · I&apos;m a Developer Advocate at Google.&lt;/p&gt;
&lt;h3&gt;Material Design, a History&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:48&lt;/strong&gt; · Material is a design system created by Google to help teams build high quality digital experiences on Android, iOS, Flutter, and the web.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · It is an adaptable system of guidelines and components and tools that support the best practices of user interface design backed by open-source code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:04&lt;/strong&gt; · Material streamlines collaboration between designers and developers to help teams quickly build beautiful products.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · Theming makes it easy to customize Material Design to match the look and feel of your brand with built-in support and guidance for customizing colors, typography, and corner shapes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · The goal with Material is to have a single unified design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23&lt;/strong&gt; · When building Material Design at Google there are multiple things we need to keep a priority, supporting multiple frameworks, ensuring spec compliance, and defining a design token API.&lt;/p&gt;
&lt;h3&gt;Material Design, a Problem&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:33&lt;/strong&gt; · So what&apos;s the solution?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:36&lt;/strong&gt; · Well, Web Components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:39&lt;/strong&gt; · They allow us to build once which is part of our philosophy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · How do Web Components solve this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44&lt;/strong&gt; · Well they&apos;re supported in all major browsers and work in every framework. And with the use of Shadow DOM it encapsulates styles from the application and stops the component styles from leaking through the existing application CSS and affecting the component styles. You can use CSS custom properties to provide an API for design tokens. You have properties that reference other properties in creating cascading tokens and effects.&lt;/p&gt;
&lt;h3&gt;Web Components FTW&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:08&lt;/strong&gt; · With that we can bring Material Design using Web Components to all of our platforms and teams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:13&lt;/strong&gt; · I&apos;m sure someone&apos;s thinking this, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:16&lt;/strong&gt; · What&apos;s this got to do with our lovely viewers?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:18&lt;/strong&gt; · Well, Material has shared the same problems as other design systems when trying to scale. We want to help you build better systems and share a little bit about what we&apos;ve learned along the way. To design at scale with Web Components we need to design once, build once, and use everywhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:34&lt;/strong&gt; · So Liz, how did Material build scalable Web Components for our own design system?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:40&lt;/strong&gt; · Let&apos;s tell a story for our audience about a company called Quaggle. Once upon a time, there was a quirky quacky gaggle of ducks with a mission to make great products. They grew into a large company with a flock of thousands of develo-ducks and dozens of teams making innovative and useful products. A group of designer ducks came together and made a strong brand for Quaggle called Bread Design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:13&lt;/strong&gt; · The ducks loved it and wanted to bring it to all of their products but they didn&apos;t know how to do so, until one day a clever duck named Quackett suggested to use Web Components. Let&apos;s go on a journey with her and look at how she and the Bread Design team built one of these components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:33&lt;/strong&gt; · Ready, Quackett?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:36&lt;/strong&gt; · Ready, Rody?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:37&lt;/strong&gt; · I&apos;m so ready, Liz.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:39&lt;/strong&gt; · All right, let&apos;s build a component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:42&lt;/strong&gt; · The first step is getting a design mock-up with all the pieces and interactions we&apos;ll need.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:47&lt;/strong&gt; · Bread Design wants Quaggle products to use the same delightful text input everywhere. At the start of the process designers provide us with mock-ups that give us the pieces of our component including a label, an input, and an optional icon. They also instruct us how the user should interact with the component. They should be able to quack text into the input.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:06&lt;/strong&gt; · Blue lines give us dimensions of each piece of the design and how they should be laid out. Some pieces of the design have really strict requirements such as the label where they can only change the text. Other parts of loose requirements such as the optional icon that can be any image icon. So Liz, what Web Component API can help us build all this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:25&lt;/strong&gt; · Well, Shadow DOM would be perfect for these complex styles. You see Shadow DOM gives us a shadow root with that we can encapsulate our Web Component&apos;s structure in its own sandbox DOM tree. Styles outside the shadow root won&apos;t affect children inside it. Normal styles inside the shadow root link to other elements on the outside. So with Shadow DOM both our styles and our clients&apos; styles are protected.&lt;/p&gt;
&lt;h3&gt;Shadow DOM&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:54&lt;/strong&gt; · On top of that within our encapsulation we can start to scale by following native element patterns such as using the same attribute, property, and event naming conventions that we see from existing HTML elements. So tl;dr if your component acts like a native element it&apos;ll work in scale really well in the frameworks and libraries out there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:15&lt;/strong&gt; · That&apos;s really great but what about the slot?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:18&lt;/strong&gt; · It&apos;s a great question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:21&lt;/strong&gt; · Slots are special elements inside a shadow root that allow you to define where the light DOM is projected. Slots can either be unnamed or named to project content with the slot attribute. The tricky thing with slots is that they can break design compliance. The projected content can be styled by the user arbitrarily so we lose control over defining what can and can&apos;t be styled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:47&lt;/strong&gt; · However, they&apos;re a vital part of Web Components and should be used to compose children. For design compliance you can use the slotted star selector along with display none to control what can and can&apos;t be used inside of a slot. All right, let&apos;s use shadow DOM and show the audience how Quaggle turned Bred Design from a mock-up into a Web Component. First let&apos;s set up a basic Web Component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:13&lt;/strong&gt; · We&apos;ll use lit-element to help us write a fast, lightweight component with efficient rendering but you can use anything to write your own Web Components from Stencil to native. We&apos;re registering our component with the custom element API that allows us to define new HTML tags. We&apos;ll call this one quack-input. Our render method is looking a little empty right now. Let&apos;s add some attributes and properties.&lt;/p&gt;
&lt;h3&gt;static get properties()&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;6:39&lt;/strong&gt; · This is an input, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:41&lt;/strong&gt; · Let&apos;s start there and add an input element. It wouldn&apos;t be much of one without a value to set though so let&apos;s follow native patterns and add a property called value. We&apos;ll bind it to our input that we just created. Now design has stated that users can change the display label so let&apos;s add a property for that. We&apos;ll need a label element to display it. We can bind our property to the text content in our render method.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:11&lt;/strong&gt; · So the label can be read by screen readers for accessibility we&apos;ll add an ARIA attribute to the input and connect it to the label. And let&apos;s not forget about the icon from the mock-up. Design told us the icon could be provided by the user though so let&apos;s make it a slot instead. Okay, looking pretty good. Lastly, let&apos;s add some interaction following the gold standard of native element patterns.&lt;/p&gt;
&lt;h3&gt;render() return html&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:41&lt;/strong&gt; · Let&apos;s listen to the change event on the input by adding a change event listener. Whenever the user types into our input, the value property should update. Finally, let&apos;s dispatch our own change event the bubbles en composes outside of our shadow root so that other elements can listen to it. That way it&apos;ll be intuitive for developers to use and compatible with frameworks. Great, we have interaction setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:09&lt;/strong&gt; · Last thing we need to do is turn our component from drab to quacktastic. Let&apos;s add some styles with lit-element static styles property. Quackett helped us here already with a bunch of styles from our mock-up but she asked us to finish up with the slotted icon. First let&apos;s hide all content by default. We can use the slotted pseudo element to select projected light DOM content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:37&lt;/strong&gt; · Next, we can add another rule to display SVG tags as well as set an explicit width and height for them so they don&apos;t pop out of our quacky little container. With this our clients can provide their icons and we&apos;ll be able to reduce how much maintenance our team takes on with what type of content is supported in our icon slot. Now that we have our component built let&apos;s meet our first team that wants to use it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:05&lt;/strong&gt; · Mallard Maps, helping ducks everywhere find the closest pond to migrate to. Mallard Maps is built with React. They want to use Bread Design&apos;s input for their migration search feature but they&apos;re worried about their app&apos;s styling being an issue. Lucky for us, Shadow DOM ensures that isn&apos;t a problem. To improve their experience, Mallard Maps is creating a wrapper React component. Some props like className and onChange are handled differently for web components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:39&lt;/strong&gt; · The wrapper will make using them seamless for Mallard Map develo-ducks. otherProps including value, onClick and more, don&apos;t need any special handling. So they can be passed directly to Quack-input. For props like onChange that require a reference to the element we can use a ref and callback hook. Let&apos;s zoom in, React&apos;s callback hook can be used to retrieve the value of a DOM element whenever it changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:11&lt;/strong&gt; · We&apos;ll use the ref hook to store the current value of the DOM element. We can add and clean up logic on the element for the props that we&apos;re wrapping such as event listeners. Now Mallard Maps can easily use QuackInput in the app with the best developer experience for their team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:30&lt;/strong&gt; · With Shadow DOM they don&apos;t need to worry about the app&apos;s complex styles interfering with QuackInput styling and our team got a thoughtful Bread loaf as a thank you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:41&lt;/strong&gt; · You know, Liz, this reminds me of something else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:45&lt;/strong&gt; · Material Design?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:47&lt;/strong&gt; · I see what you did there, but no. I mean, using the Shadow DOM encapsulation attributes and properties and slots as a powerful API to scale and manage your design system. It reminds me of a web component set that I&apos;ve seen the wild jungles of NPM node module trees, Lion Web Components. These are white label components built by ING in a design system that can be used by anyone.&lt;/p&gt;
&lt;h3&gt;Lion Web Components by ING&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;11:10&lt;/strong&gt; · These components use slots and attributes to set up pieces that can be customized in an extended design system like ING web. On the left we have the white label components and on the right we have the first-party components. These components can be used as a starting point for your own design system. When extending Lion Web Components you can use both attributes and/or slots to customize what is shown.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:32&lt;/strong&gt; · You can even check them out at the QR code right here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:36&lt;/strong&gt; · All right, Liz, what&apos;s next with Shadow DOM?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:38&lt;/strong&gt; · What do you mean ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:40&lt;/strong&gt; · Isn&apos;t accessibility a problem with reference IDs?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:42&lt;/strong&gt; · Also I tried using a Web Component in a form and it didn&apos;t submit like the other inputs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:47&lt;/strong&gt; · Those are great points, Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:49&lt;/strong&gt; · We can use ElementInternals in the Accessibility Object Model or AOM to help address these needs. We can call attachInternals on a custom element and get an ElementInternals instance which provides many internal features to help integrate our web component with platform features such as forms and Accessibility Object Model.&lt;/p&gt;
&lt;h3&gt;class MyElement extends HTMLElement constructor()&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;12:13&lt;/strong&gt; · The AOM itself is an exciting new set of features which include reflecting ARIA properties and attributes, reflecting element references, and providing default semantics for custom elements through the ElementInternals&apos; interface. ElementInternals and the describe features of the Accessibility Object Model are ready to use on Chrome and Edge today. AOM ARIA attribute reflection is also ready to use on Safari.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:43&lt;/strong&gt; · All right, let&apos;s take a look at how we can add ElementInternals and the Accessibility Object Model to the Web Component we just made. Let&apos;s start with form integration. To register our component as formAssociated, we need to add a static formAssociated property. This tells the browser that our custom element is ready to participate in forms. The last thing we need to do is set the form&apos;s value. We can do that in the element&apos;s update lifecycle.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;static get formAssociated() { return true; }&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;13:11&lt;/strong&gt; · Whenever our value property changes we can update the form value as well using ElementInternals. That was easy; well, buckle up ducklings. ARIA and accessibility can get a bit tricky. Always remember the golden rule act like native HTML elements. That means a user should be able to set the aria-labelledby attribute on our component and everything should work. So let&apos;s start there and work backwards. We&apos;ll need to know when that ARIA attribute changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:46&lt;/strong&gt; · Custom elements have a static property called observed attributes that allow us to list which attributes we want to listen to. Normally lit-element manages this for us. So we&apos;ll need to call super to keep that behavior while adding the extra attribute we want. Next let&apos;s override the custom element callback for that static property: attributeChangedCallback.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:09&lt;/strong&gt; · Whew, they really picked long names for these, huh?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:13&lt;/strong&gt; · We only care about the attribute that is aria-labelledby attribute. Everything else can be passed off to lit-element&apos;s implementation in the superclass. Okay finally, let&apos;s use the Accessibility Object Model. Since AOM reflects reference IDs to elements themselves we can use the ariaLabelledByElements property to get the actual elements that the ID or IDs in the attribute refer to and we didn&apos;t have to do anything for them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:41&lt;/strong&gt; · Pretty neat, huh?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:43&lt;/strong&gt; · With that, we can use the same property and pass those elements off to our shadowRoot&apos;s input since that&apos;s what the screen reader will be focusing on and what the elements should be labeling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:53&lt;/strong&gt; · Hmm, but what should we do about our host Web Components attribute?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:58&lt;/strong&gt; · We should probably remove it. Otherwise the screen reader will announce the labels twice and that would just confuse a bunch of ducks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:07&lt;/strong&gt; · Wait, what is it, Quackett?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:10&lt;/strong&gt; · Oh, good point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:12&lt;/strong&gt; · If we remove our attribute our change callback is going to be called again. We&apos;ll need to add a flag and gate our logic so that we don&apos;t accidentally remove our input&apos;s labels after just adding them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:24&lt;/strong&gt; · Good catch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:26&lt;/strong&gt; · Let&apos;s see this in action.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:28&lt;/strong&gt; · Quackett, who&apos;s next on our list of teams to visit?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:33&lt;/strong&gt; · Oh, of course, that&apos;s right. I remember who needs these features, POND. A Quaggle Travel company helping ducklings find an Island with a pond to call their own. POND is built with VUE and they have booking forms that Bread Design&apos;s input would be perfect for. They want to make sure it will properly submit with their forms. Additionally, they want to use their own external labels with the inputs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:02&lt;/strong&gt; · Those labels will need to be hooked into our inputs so they can be read by screen readers for ducks with accessibility requirements. POND works on a loaf exchange system. Here&apos;s one of their VUE components that is using our quack-input to pay for a travel trip. Our golden rule seems to be paying off. Our quack-input is nearly indistinguishable from a native input.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:25&lt;/strong&gt; · To use it inside of a form a name attribute must be provided, just like other form elements. When the form submits quack-input&apos;s value is associated with that name key. When using the aria-labelledby attribute and an external label&apos;s ID screen readers announce the external label even though the browser is focused on an input element deeply nested inside the shadowRoot of the host element. That was pretty successful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:52&lt;/strong&gt; · Well, Rody, we have our component structure but I think it could use a splash of color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:57&lt;/strong&gt; · What&apos;s next from design?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:59&lt;/strong&gt; · Well, Bread Design has provided us with design tokens to specify colors, shape, and topography. Some tokens inherit from each other. For example, a label primary color inherits from the amber color. To start off, they gave us the tokens to implement the light theme and also the dark theme with the correct tokens for each.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:16&lt;/strong&gt; · So Liz, since you had such a good answer last time, what is the Web Component API that can help us out here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:22&lt;/strong&gt; · Of course we can use CSS Custom Properties. Custom Properties are user-defined variables. We can define a Custom Property with a value and then use that value in a CSS declaration. Custom Properties can also provide fallback values to use for when they have not been defined. The best part is that Custom Properties can be overridden so that you can dynamically change the value of a declaration at runtime.&lt;/p&gt;
&lt;h3&gt;CSS Custom Properties&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;17:51&lt;/strong&gt; · This is why CSS Custom Properties are perfect for design tokens. Each Custom Property can match one to one with individual design tokens. Properties can inherit from other properties as well, just like our tokens do. And finally, you can apply in scoped Custom Properties to specific sections of a DOM tree using simple CSS selectors for incredible flexibility and a satisfying runtime customization experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:19&lt;/strong&gt; · So Liz, what about the CSS part API?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:22&lt;/strong&gt; · Can we just use that instead of all the Custom Properties to expose our theming?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:26&lt;/strong&gt; · It&apos;s another great question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:28&lt;/strong&gt; · In a custom element shadow root, pieces of the shadow DOM can be exposed using the part attribute. Clients can then use the part suitor element to add any CSS declarations to the part. As you can guess, parts are similar to slots. They can potentially break design compliance and expose arbitrary styling on pieces of a component. This can be good if your design system allows one-off customizations since it can mean fewer custom properties.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:00&lt;/strong&gt; · However, it can also be difficult to maintain if your design system wants strict compliance. Especially with larger teams where there&apos;s less direct control over what clients are doing, like Quaggle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:13&lt;/strong&gt; · For example, could a user make quack-input&apos;s label bold?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:17&lt;/strong&gt; · If so, we could expose a part and allow them to set the font weight. However, they&apos;re also able to change the font size, add padding. So it could be dangerous exposing a part if we can&apos;t enforce what declarations are used. Without it, we need to add another Custom Property so users could change the font weight. So tl;dr part means pure custom properties but potentially more maintenance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:44&lt;/strong&gt; · All right, let&apos;s dive in and bring design tokens to quack-input with the help of Custom Properties. We&apos;ll begin with color, that&apos;s a good place to start. Our labeling input should be the primary design token color. According to our design token inheritance the primary color should be Amber 700. So we can add that in as a primary tokens fallback value. Let&apos;s work on topography next, We have quack-input specific tokens for that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:14&lt;/strong&gt; · The label and input share the same font family but they each have their own token for their respective font sizes. All right, pretty good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:23&lt;/strong&gt; · Now what should we do about dark theme?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:27&lt;/strong&gt; · I think I can help with that, Liz. Here we can use the media query prefers-color-scheme to detect if the user has turned on a dark mode on their device. Now all we need to do is to find the primary design token as the color provided in our dark theme mock-ups. There&apos;re a couple of reasons to use the prefers-color-scheme which includes better accessibility, automatic dark mode switching and power savings with OLED displays.&lt;/p&gt;
&lt;h3&gt;Emedia (prefers-color-scheme: dark)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;20:53&lt;/strong&gt; · You can check out the web.dev link here to read the blog post to learn more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:57&lt;/strong&gt; · Wow, thanks Rody.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:59&lt;/strong&gt; · Okay, we&apos;ve got the colors for our component and the means to change them. I know the next team that desperately wants to use these new features, QuackTube, making ducks stars or just helping them find fish memes. QuackTube is built with Angular. They want to use Bread Design&apos;s input for their comments section but they have unique brand colors that are different from Quaggle&apos;s default brand. See they&apos;re a sister company under the larger Waterfowl Corporation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:33&lt;/strong&gt; · Lucky for us though, design tokens were specifically designed for this. Custom Properties will let QuackTube override their default colors to match what they need. First things first, in Angular, don&apos;t forget to add the custom element SCHEMA to the NgModules that are using custom elements that tells Angular to expect element tag names it wouldn&apos;t immediately recognize. All right, let&apos;s get started. First, let&apos;s look at our comment component using quack-input.&lt;/p&gt;
&lt;h3&gt;Component selector comment-input&apos;, template&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;22:08&lt;/strong&gt; · Everything looks pretty standard for an Angular component and its template, as well as what we&apos;ve seen so far. So let&apos;s move on to the styles. That&apos;s where QuackTube wants to change things up. QuackTube develo-ducks have added a bunch of custom property declarations. With a simple CSS selector they&apos;re able to scope the changes they wanted to make, including color and typography.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:30&lt;/strong&gt; · Using Custom Properties is actually pretty straightforward, way to go QuackTube. You know Liz this is starting to ring a bell for me too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:39&lt;/strong&gt; · It&apos;s kind of like Material Web Components, huh?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:42&lt;/strong&gt; · (laughs) True.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:44&lt;/strong&gt; · But I was meaning the Custom Properties for design tokens. There&apos;s some great teams out there doing the same thing with their design systems like Adobe Spectrum Web Components. Spectrum is Adobe&apos;s design system that defines many tokens including those that inherit from each other. Spectrum Web Components uses Custom Properties to implement their design tokens. Tokens are set and provided via sp-theme helper element rather than being set manually by the user.&lt;/p&gt;
&lt;h3&gt;Spectrum Web Components by Adobe&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;23:10&lt;/strong&gt; · It&apos;s a great resource if you want to see a pre-built design system like Spectrum or just reference it for inspiration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:16&lt;/strong&gt; · So is this when I ask what&apos;s coming next, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:19&lt;/strong&gt; · You mean when you mention all the problems with Custom Properties?&lt;/p&gt;
&lt;h3&gt;Problems, problems&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;23:23&lt;/strong&gt; · Yeah, users can provide any value to a Custom Property and it may not even be the right one. It&apos;s also hard to maintain all the extra CSS and make sure all the default values are set in a proper token inheritance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:35&lt;/strong&gt; · True, luckily there&apos;s a new technology called Registered Properties that can help us with these problems. Custom Properties can be registered with the browser using the app property rule in CSS or CSS.registerProperty in JavaScript. We can set the property syntax to many types such as links, numbers, URLs, images, and of course colors. The syntax will make our Custom Property behave like a native user agent property. If the user provides an invalid value the declaration will be ignored.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:08&lt;/strong&gt; · Whereas unregistered custom properties would completely drop the declaration and result in an unstyled element. We can also set whether or not their property is inherited between a child element and its parent, like the color property or if it&apos;s not, like the border property. Finally, we can give our custom property an initial value. The browser will use this even if we have not provided a value declaration or fallback for our property.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:38&lt;/strong&gt; · Register Properties are ready to be used in Chrome and Edge today. Let&apos;s take a look at how we can add Registered Properties to our component to clean things up. This is what quack-input&apos;s CSS currently looks like. We have a lot of fallback values for inheritance between design tokens and their initial values. It&apos;s pretty hard to read or maintain, so let&apos;s clean it up. For Bread Design&apos;s Components we&apos;ll provide a global style sheet that clients can import.&lt;/p&gt;
&lt;h3&gt;Bread Design global styles&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;25:08&lt;/strong&gt; · It will register our design token Custom Properties as well as define the inheritance between them. Let&apos;s register our static Amber 700 token first with its initial value. We&apos;re setting it to inherits false because we don&apos;t anticipate users changing the value of this token. So there&apos;s no need for the browser to spend extra effort calculating the inheritance. Next we&apos;ll register our primary token.&lt;/p&gt;
&lt;h3&gt;@property ---amber700 syntax: &apos;&apos;; inherits: false&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;25:35&lt;/strong&gt; · It will be set to inherit because it&apos;s a color token and we want users to set it once on a parent element and let the value inherit through all of the children of that element. Finally, let&apos;s establish the relationship between these two design tokens. We&apos;re using the root selector because it&apos;s less specific than the HTML tag selector. Just in case our clients want to override our initial definitions, and let&apos;s not forget about dark mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:03&lt;/strong&gt; · We can move our dark mode styles from inside our Web Component to this global style sheet since all of our components will be dark mode compatible. Let&apos;s look at how this changed quack-input CSS, much better. This will be a lot easier to maintain and result in a smaller file size, especially as the number of design tokens and web components increase. So let&apos;s revisit QuackTube and see what has changed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:30&lt;/strong&gt; · Actually not much, they&apos;re still using the same design tokens except now they have a smaller bundle size, which means faster loading for their users. Additionally, they receive improved performance from browser optimizations that Registered Properties bring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:46&lt;/strong&gt; · That&apos;s pretty impressive, Liz.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:47&lt;/strong&gt; · So where does that leave Quackett in her own story?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:51&lt;/strong&gt; · Come on, Quackett, let&apos;s do a quick recap. Quaggle now has a solid foundation of Web Components for Bread Design that were built once and can be used everywhere. Shadow DOM ensures compliance with a unified brand and Custom Properties provide a design token API for all develo-ducks customization needs and, Quackett, she gets a well-deserved vacation, good job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:19&lt;/strong&gt; · Nice work, Quackett.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:22&lt;/strong&gt; · Well, if you hadn&apos;t figured it out Quaggle and Bread Design is a story of Google and Material Design. We face with the problems of bringing Material Design to Google&apos;s Teams, Maps, Travel, YouTube, and more. We encounter many of the same problems that other design systems face when scaling, supporting multiple frameworks, ensuring spec compliance, and providing a robust API for customizing our design tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:45&lt;/strong&gt; · Web Components allow our design systems to scale on the web and we hope our discoveries can help your design system scale too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:51&lt;/strong&gt; · All of the code from this talk, including quack-input and examples for setting up Web Components in each of the frameworks mentioned is available to view and play around with. You can also find out more about what&apos;s coming next with the future of Material Web Components. We&apos;re really excited about the direction Material on the web is going and we hope you check it out. Thanks for tuning in, I&apos;m Liz.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:14&lt;/strong&gt; · And I&apos;m Rody, and remember Design once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:16&lt;/strong&gt; · Build once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:17&lt;/strong&gt; · Use everywhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:20&lt;/strong&gt; · (upbeat music)&lt;/p&gt;
</content:encoded></item><item><title>Findevs - Flutter Hackathon 2019 - Orlando Winner</title><link>https://rodydavis.com/videos/talks/findevs-flutter-hackathon-2019-orlando-winner/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/findevs-flutter-hackathon-2019-orlando-winner/</guid><description>Findevs is a marketplace hub that connects Flutter developers with clients, providing a centralized platform for service discovery, project logging, and developer monetization.</description><pubDate>Mon, 03 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Findevs - Flutter Hackathon 2019 - Orlando Winner&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;cOgYFY6fJiU&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Source: https://github.com/AppleEducate/flutter_devs/tree/hackathon&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · so hi guys welcome to the hackathon intentionally or not invincible I got to be part of this so we build an application which kind of is we thought&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:17&lt;/strong&gt; · that there was no place it doesn&apos;t work okay so there was it wasn&apos;t a petition out there that is kind of like like a hub for developers and those that are client that are in need of flutter pigeons for other apps and stuff that you can do with Clark so and also there wasn&apos;t enough motivation for lot of people to come and join and learn how to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:47&lt;/strong&gt; · code in planner and they just didn&apos;t have a place where they can discover authoritative so we created index which is a hub that connects flutter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:07&lt;/strong&gt; · developers and but also clients that need their services okay so this application is simple like client side and developer side logging application we have both on the more about that in grody&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33&lt;/strong&gt; · so we&apos;re gonna go ahead and start the demo so like we said this is going to be an app for the developer and the client to have one place to interact with each other so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57&lt;/strong&gt; · and like we said earlier too this is really important because we want to monetize and help to developer to make money because they&apos;re creating their stuff but then you also want the clients to request anything and be able to be in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:12&lt;/strong&gt; · contact with one so here we have our application and this is our logo at the top and then when you start and login you&apos;re greeted with a client we&apos;re developer logging so let&apos;s go ahead and start as a client so you&apos;ll see all your current projects and you can drill down into each one of them you can see how long ago you created it as well as how many project hours and the perfect name as well as description so let&apos;s go ahead and add a new project&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:50&lt;/strong&gt; · and you can give it a description and give it a price you can do through $200 for this one and I&apos;ll say this is gonna be 8 hours and you have you can choose whether or not you want it to be open source so let&apos;s go ahead and hit save and as you see it&apos;s added to your queue of current projects so that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:14&lt;/strong&gt; · great now so now what the reverse side is where the developer comes in so if you log in as a developer you&apos;ll be able to see this app and claim the application that means that you&apos;ll be able to start work on you can also additionally sort by different fields so you can sort by date created if you want to see the newest projects or if you wanted to see the oldest ones first you can also choose by&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:44&lt;/strong&gt; · the bouncy mill so if you would just just want to do the the highest value or if you want to start with the cheapest ones you can do that as well and our final one is ours so you can just start with the shortest project which is&lt;/p&gt;
</content:encoded></item><item><title>Google Cloud Next 2022, Dart partnership with GitHub, and more dev news!</title><link>https://rodydavis.com/videos/talks/google-cloud-next-2022-dart-partnership-with-github-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/google-cloud-next-2022-dart-partnership-with-github-and-more-dev-news/</guid><description>This developer news show covers recent updates, including Dart&apos;s new integration with GitHub for supply chain security, details from Google Cloud Next 2022, and information on machine learning applications like CircularNet.</description><pubDate>Thu, 13 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Google Cloud Next 2022, Dart partnership with GitHub, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;Ay5O5H9MAlI&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 315 | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:10 - Partnering with GitHub on supply chain security for Dart packages → https://goo.gle/3EKj2Fs
0:32 - Google Cloud Next 2022 → https://goo.gle/3EKiXSa
0:46 - CircularNet: Reducing waste with Machine Learning → https://goo.gle/3CUjr7g
1:20 - Kick Start Round G → https://goo.gle/3ET648P
1:51 -  Don’t forget to like, comment, and subscribe!&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody from Developer Relations. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow&lt;br&gt;
Subscribe to Google Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [AUDIO LOGO] RODY DAVIS: Hi, I&apos;m Rody, for The Developer Show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:06&lt;/strong&gt; · And this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;h3&gt;Partnering with GitHub on supply chain security for Dart packages&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · Let&apos;s start with some exciting new Dart news.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:12&lt;/strong&gt; · Starting today, GitHub now supports Dart in the advisory database, dependency graph, and Dependabot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:18&lt;/strong&gt; · This means it&apos;s now possible to fully support supply chain security for Dart and Flutter apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:23&lt;/strong&gt; · If there&apos;s an issue detected, a pull request will be automatically created with the patch for your project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · You can find out more on the GitHub blog link below.&lt;/p&gt;
&lt;h3&gt;Google Cloud Next 2022&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:32&lt;/strong&gt; · Make sure to attend the last day of Google Cloud Next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:34&lt;/strong&gt; · It is free to register, and there&apos;ll be some exciting news about what is coming next for Google Cloud.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:39&lt;/strong&gt; · There&apos;ll also be a lot of developer-specific content in the Developer Zone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:42&lt;/strong&gt; · You can find out more on cloud.withgoogle.com.next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:45&lt;/strong&gt; · Next, we head over to TensorFlow to talk about the new circular net model and how it&apos;s being used to help recycle waste.&lt;/p&gt;
&lt;h3&gt;CircularNet: Reducing waste with Machine Learning&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:53&lt;/strong&gt; · This model is being trained on thousands of images from the mass RCNN algorithm, which is implemented from the TensorFlow garden.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:00&lt;/strong&gt; · By collaborating with experts in the recycling industry, it is now possible to annotate the data in the model that is used for different material types, such as paper, metal, plastic, and even specific plastic types.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:11&lt;/strong&gt; · The goal is to collaborate with stakeholders across the waste management ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · So if you&apos;re interested, reach out to waste innovation external@google.com.&lt;/p&gt;
&lt;h3&gt;Kick Start Round G&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:21&lt;/strong&gt; · Have you tried Kick Start yet?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · It&apos;s not too late.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23&lt;/strong&gt; · Join us for a penultimate Kick Start round on the season on October 15.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:26&lt;/strong&gt; · Kick Start is a coding competition that aims to help coders strengthen their programming skills while having fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:32&lt;/strong&gt; · Tackle interesting algorithmic problems and test your skills.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:35&lt;/strong&gt; · There&apos;s only two rounds left this year.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:37&lt;/strong&gt; · So don&apos;t miss out on all the fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:38&lt;/strong&gt; · Head over to g.co/kickstart to learn more and sign up for the round G taking place on October 15.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:44&lt;/strong&gt; · Or check out @GCodingComps on Twitter for the latest updates and info on Google&apos;s coding competitions.&lt;/p&gt;
&lt;h3&gt;Don’t forget to like, comment, and subscribe!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:51&lt;/strong&gt; · To learn more about all this week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:56&lt;/strong&gt; · And please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:59&lt;/strong&gt; · I&apos;m Rody for The Developer Show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · Thanks for watching, and I&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:05&lt;/strong&gt; · [LAUGHTER]&lt;/p&gt;
</content:encoded></item><item><title>Google I/O 2023, passkeys passwordless future, and more dev news!</title><link>https://rodydavis.com/videos/talks/google-io-2023-passkeys-passwordless-future-and-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/google-io-2023-passkeys-passwordless-future-and-more-dev-news/</guid><description>Google Developer News reviews the shift to secure passkeys for a passwordless future, providing key takeaways from Google I/O 2023 and essential Android development tips.</description><pubDate>Mon, 08 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Google I/O 2023, passkeys passwordless future, and more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;GfUvCl4uCWc&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 340  | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:10 - The beginning of the end of the password → https://goo.gle/41gLgPO
For developers you can learn more → https://goo.gle/3VHW608
For users you can learn more at g.co/passkeys
0:57 - Become a Google Developer Student Club lead → https://goo.gle/3HOVOin
1:27 - How to optimize your Android app for large screens (And what NOT to do!)→ https://goo.gle/3LN6GOS
2:23 -  Get ready for I/O ‘23 → https://goo.gle/3VJWawu
2:46 -  Don’t forget to like, comment, and subscribe!&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody, a Developer Advocate. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow&lt;br&gt;
Subscribe to Google Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [MUSIC PLAYING] RODY DAVIS: Hi, I&apos;m Rody for The Developer Show, and this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;h3&gt;The beginning of the end of the password&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · If you&apos;ve ever had trouble forgetting your password or coming up with strong passwords and changing them often, then you&apos;ll be really excited to learn about passkeys.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:18&lt;/strong&gt; · Today, passkeys are enabled for Google accounts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · They are more secure than passwords and will work with some of your favorite services like PayPal, DocuSign, and Shopify.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:27&lt;/strong&gt; · Unlike passwords, passkeys only exist on the device where you created them, and if you set up a passkey, you have to do it on each device.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:35&lt;/strong&gt; · Some platforms can securely back up your passkeys and sync them to other devices you own.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:40&lt;/strong&gt; · If you lose a device, you can immediately revoke the passkey in your Account Settings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:44&lt;/strong&gt; · For developers, you can learn more at developers.googl e.com/identity/passkeys on how to integrate them into your applications, and for users, you can learn more at g.co/passkeys.&lt;/p&gt;
&lt;h3&gt;Become a Google Developer Student Club lead&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:57&lt;/strong&gt; · If you love programming and Google technologies, and are interested in your tech skills to help your community, and are excited to teach other students to code then we encourage you to apply to be a Google Developer Student Club Lead at your university.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:10&lt;/strong&gt; · As a lead, you&apos;ll receive mentorship from Google, join a global community of leaders, share your skills with peers, teach other students to code with Google technologies, and build solutions for real world problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:23&lt;/strong&gt; · Check out the regional application window and apply at the link below.&lt;/p&gt;
&lt;h3&gt;How to optimize your Android app for large screens (And what NOT to do!)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:27&lt;/strong&gt; · Next, we can head over to android to talk about the ways you can optimize your Android app for large screens and what not to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34&lt;/strong&gt; · There are a lot of resources Google provides for the development of large screens, and even if you don&apos;t have a device to test on, there&apos;s plenty of virtual ones to emulate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:42&lt;/strong&gt; · Some of the important takeaways from the article are-- don&apos;t assume exclusive access to resources; do handle hardware access gracefully; do respond appropriately to lifecycle events; don&apos;t stop your app&apos;s UI on a pause; don&apos;t rely on device-type Booleans like isTablet; do work to replace existing use of device-type Booleans with the right approach; do use the windowInsetsPadding Modifier for Jetpack Compose; don&apos;t assume the device has a touch screen; do test your app on large screens; do leverage large screen tools in Android Studio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:18&lt;/strong&gt; · You can read more about these optimizations on the Android Developers blog.&lt;/p&gt;
&lt;h3&gt;Get ready for I/O ‘23&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:23&lt;/strong&gt; · And finally, you can start getting ready for Google I/O 2023 by starting to plan your sessions and taking a look at some of android&apos;s favorite moments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:30&lt;/strong&gt; · Stay tuned for some amazing content, such as-- modern android development, building for a multi-device world, the platform and app quality, and finally, android&apos;s road to I/O 2023.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:42&lt;/strong&gt; · You can learn more at developers.googleblog.com.&lt;/p&gt;
&lt;h3&gt;Don’t forget to like, comment, and subscribe!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:46&lt;/strong&gt; · To learn more about all these week&apos;s stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:50&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:54&lt;/strong&gt; · I&apos;m Rody for The Developer Show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:55&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:57&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Material You &amp; Figma: Bringing Dynamic Color &amp; brand together - Rody Davis, Ivy Knight (Schema 2021)</title><link>https://rodydavis.com/videos/talks/material-you-figma-bringing-dynamic-color-brand-together-rody-davis-ivy-knight-schema-2021/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/material-you-figma-bringing-dynamic-color-brand-together-rody-davis-ivy-knight-schema-2021/</guid><description>This session explores how Material You&apos;s dynamic color features can integrate with distinct brand identities, demonstrating the use of a new Figma plugin to generate and apply cohesive dynamic color schemes within a design system.</description><pubDate>Wed, 10 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Material You &amp;amp; Figma: Bringing Dynamic Color &amp;amp; brand together - Rody Davis, Ivy Knight (Schema 2021)&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;clDuqcKgNBQ&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Material You is enabling a new level of individuality across interfaces. But how does dynamic color interact with the distinct brand expression in your design system? In this session, Rody Davis (Developer Advocate) and Ivy Knight (Designer Advocate) dive into the NEW Figma plugin for generating dynamic color schemes and see those changes reflected throughout your app&apos;s UI.&lt;/p&gt;
&lt;p&gt;Schema is an online conference about design systems by Figma. For more info, check out https://schema.figma.com/&lt;/p&gt;
&lt;p&gt;#figma #schema #schema2021 #design #ProductDesign #FigmaDesign #DesignSystems #MaterialYou #Google #GoogleDesign&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:02&lt;/strong&gt; · [Rody] Hi I&apos;m Rody Davis, a Developer Advocate for Material Design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:06&lt;/strong&gt; · [Ivy] And I&apos;m Ivy Knight, a Designer Advocate for Material Design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:09&lt;/strong&gt; · [Rody] Our team focuses on helping the community build beautifully with Material Design by providing resources, tooling and examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:16&lt;/strong&gt; · [Ivy] Material Design is a design system created by Google backed by open source code to help teams build high quality, digital experiences for Android flutter and the web.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:28&lt;/strong&gt; · It includes design guidelines, and an extensive library of components that can be customized to meet your style and brand needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:37&lt;/strong&gt; · Since its formation, Material Design has relied on color to express hierarchy and communicate meaning in UI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:45&lt;/strong&gt; · A primary color appeared more frequently in an app, while secondary colors were used to accent key parts of your UI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:53&lt;/strong&gt; · Throughout the years, we&apos;ve expanded on colors to be more expressive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · Building off Material Designs primary and secondary colors, Material Design 3 introduces tertiary and container colors to express a harmonious set of tonal palettes across your UI the check for accessibility.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:13&lt;/strong&gt; · This year at Google I/O, we showed the bold and expressive evolution of Material Design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · We&apos;re excited to be here to show you how to implement Material You in your digital experiences, and the tooling we created to support designers and developers with dynamic color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31&lt;/strong&gt; · Color is a foundational element in any design system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34&lt;/strong&gt; · Material You reimagines color as a more individualized experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:41&lt;/strong&gt; · Dynamic color lets personal devices feel personal, and we believe design systems should be just as personal as the devices they appear on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · By incorporating personal preferences in the UI of your product, users have more control than ever over their devices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · With dynamic color and Material 3 color schemes, your app&apos;s color automatically adapt and integrate with user colors by letting their wallpaper influences the app&apos;s color scheme We understand this is a new way of thinking of colors for your app The digital products we create can now reflect trends in our real life products that adopt more personal colors, patterns and materials.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:29&lt;/strong&gt; · Dynamic color embraces the need for color to reflect an app&apos;s design sensibility while also honoring an individual&apos;s needs and preferences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:39&lt;/strong&gt; · It&apos;s the key part of Material You, in which an algorithm derives colors from a user&apos;s wallpaper to be applied to their apps and system UI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:52&lt;/strong&gt; · Since color schemes are defined by tonality, rather than a hue or a hex value, the system of tonal palettes essential to making any color scheme accessible by default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:04&lt;/strong&gt; · Using a minimum 60 luminance spread in color pairings, color combinations provide enough contrast to ensure accessibility standards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:15&lt;/strong&gt; · For example, these colors may have a different hue to them but the tone is very similar making contrast inaccessible, especially for those with certain color blindness and at the bottom here, here&apos;s the same concept applied to a fab and with the corrected versions to the right of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:35&lt;/strong&gt; · The color system automatically assigns the appropriate hue, chroma and tonal values for all roles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:43&lt;/strong&gt; · How does this all work?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:49&lt;/strong&gt; · Starting with the user&apos;s wallpaper, a source color is extracted and extrapolated into five key colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:57&lt;/strong&gt; · Each color is then interpreted into a tonal palette of 13 tones and a selection of colors from the tonal palettes are then slotted to a group of specific roles and values that map to components called a scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:14&lt;/strong&gt; · Color schemes can be considered a cohesive group of relative tones rather than a fixed group of constant values.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:23&lt;/strong&gt; · Light and dark schemes are generated for each Semantic colors such as air color is also slotted automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:34&lt;/strong&gt; · Each color roll required for the UI is derived from the key colors using this process, ensuring proper contrast between elements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:45&lt;/strong&gt; · These roles are what you will map to in your designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:50&lt;/strong&gt; · You may be familiar with the current 12 color slots in Material Design, like primary and on primary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:57&lt;/strong&gt; · With Material Design 3 or M3, we introduce a new tonal palette and roles and schemes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:05&lt;/strong&gt; · Container and its respective on container used for UI elements that do not need as much emphasis as their non-container counterparts and a new tonal palette, tertiary, used to bring broader color expression in your product.&lt;/p&gt;
&lt;h3&gt;Design Tokens&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:21&lt;/strong&gt; · [Rody] But how do we make this all possible?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:24&lt;/strong&gt; · Well, it&apos;s through design tokens and this allows us to change role assignment that cascade consistently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:33&lt;/strong&gt; · Building with values that change at runtime means that we need to reference colors by their semantic meaning, instead of hard-coded values.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:40&lt;/strong&gt; · Building off of the Material Design to color roles, tokens provide global styling slots as well as a token being able to have multiple types paired with a value or referencing another token.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:50&lt;/strong&gt; · With Material 3, we now have the concept of palette reference and system tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:56&lt;/strong&gt; · These color rolls you build with are the system tokens and they can inherit from the reference tokens, which we are introducing with called tonal palettes and these include primary, secondary, tertiary, neutral, neutral variant, and error Design tokens enable flexibility and consistency across a product by allowing designers to assign an element&apos;s color role in a UI, rather than a set value which then be handed off to a developer to implement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:26&lt;/strong&gt; · Design tokens can be generated, saving the developer and designer time by agreeing on a source of truth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:32&lt;/strong&gt; · A developer can then reference this file and map to the theming object, for example in compose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:38&lt;/strong&gt; · If these tokens change in code, this can now easily be shared back with the designer by having the values updated in their design mocks Swapping colors and typography hard-coded values with tokens should make it easier for you to iterate on your designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:54&lt;/strong&gt; · Colors in a tonal palette are mapped to light or dark scheme through design tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:59&lt;/strong&gt; · The color scheme values can be overridden to inherit custom colors, or just the reference tokens themselves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:08&lt;/strong&gt; · User generated color utilizes these color mappings to tokens to create dynamic and expressive UIs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:17&lt;/strong&gt; · When the system colors change at run time, they will update the tonal palettes which then update the color schemes, which is what you use to map to your components in your theme It is important to use the right color roles for the corresponding component to ensure accessibility and continuity.&lt;/p&gt;
&lt;h3&gt;Material Theme Builder&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:34&lt;/strong&gt; · We needed a way for app creators to explore dynamic color, and we understand that the color system via design tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:41&lt;/strong&gt; · So we built a tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:44&lt;/strong&gt; · And we&apos;re so happy to introduce the Material Theme Builder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:48&lt;/strong&gt; · A tool to help designers and developers create custom color schemes, and visualize dynamic color in their UI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:56&lt;/strong&gt; · We knew introducing dynamic color brought on the new challenge in how we thought about and approach color in our designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:03&lt;/strong&gt; · We wanted to provide designers and developers hands-on tools to explore user-generated dynamic color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:11&lt;/strong&gt; · This tool can help visualize how it may be reflected in their UI, and can be integrated into their existing workflows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:19&lt;/strong&gt; · We built it for Figma and the web.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:21&lt;/strong&gt; · Material Theme Builder creates Material Design tokens as Figma styles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:26&lt;/strong&gt; · These tokens are automatically mapped to our components within our Material Design Kit, and can easily be mapped to your custom elements within your Figma designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:35&lt;/strong&gt; · As Design System Advocates, it was important for tooling to act as educational guidance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:40&lt;/strong&gt; · Showing the process on how a source color generates tonal palettes, and how these mapped to color schemes helps creators understand the process and magic powering it all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:49&lt;/strong&gt; · We were developing this tool in parallel with guidance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:52&lt;/strong&gt; · We tested it early and often with internal teams, and this led to fast iterations based on feedback.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:58&lt;/strong&gt; · In its early inception, the tool was primarily focused on visualizing dynamic color but the complexity of dynamic color is that the generated color scheme doesn&apos;t paint a complete picture of the various ways your UI color can change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:11&lt;/strong&gt; · It became clear that managing themes was an absolute necessity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:15&lt;/strong&gt; · In order to make iteration easy for designers, we created a way to swap themes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:20&lt;/strong&gt; · By using the same token prefix, creators are able to swap easily between various themes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:25&lt;/strong&gt; · Each theme is validated to check for missing tokens on launch, which meant being able to use the plugin to test and create faster themes within our design mocks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:35&lt;/strong&gt; · But all the tokens have a structured format, which means that we get to preserve the metadata by switching themes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:42&lt;/strong&gt; · This also allows for toggling between light and dark themes as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:45&lt;/strong&gt; · This also means that managing designs consisting of multiple brand themes was a breeze.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:51&lt;/strong&gt; · There&apos;s no more manually diving into the style pickers, setting colors, and this now can be done by just swapping to a new theme, hitting swap and now the plugin will be recognized because of the Material Design tokens.&lt;/p&gt;
&lt;h3&gt;Demo&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;10:02&lt;/strong&gt; · We wanted to make generating from an image easy for designers, so we store the image as a color style and load it in on launch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:10&lt;/strong&gt; · Alright let&apos;s see all this in action.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:12&lt;/strong&gt; · Jumping into our Figma file with the Material Theme Builder open.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:16&lt;/strong&gt; · We can open it up and connect it to our design kit components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:21&lt;/strong&gt; · Here we have the plugin, and we&apos;re just going to look and see what the default canvas is outputted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:27&lt;/strong&gt; · We&apos;re going to select an image, and have it run through the algorithm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:33&lt;/strong&gt; · With an image selected, it updates the image style token and generates the new colors on the canvas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:40&lt;/strong&gt; · Here we can see the colors, key colors that are generated as well as the light and dark schemes from the tonal palettes, and the surfaces.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:53&lt;/strong&gt; · The plugin works with our design kit allowing you to select any arbitrary selection, and click on swap to have it migrate to the tokens of your current selected theme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:08&lt;/strong&gt; · You can quickly iterate on these colors by choosing new images, which we&apos;ll do now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:16&lt;/strong&gt; · This makes it really easy to iterate on your designs, while having a single set of tokens and new images that are stored in those seed values.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:28&lt;/strong&gt; · Here we can see all the colors change, but our moc stays the same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:38&lt;/strong&gt; · So beyond visualizing dynamic color, we need a way for designers and developers to migrate to Material 3&apos;s new color system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:47&lt;/strong&gt; · In this new color system, we have an entire scheme that&apos;s derived with tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:54&lt;/strong&gt; · So here, we can update our seed color to generate these new color schemes with this new shuffle button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:03&lt;/strong&gt; · This allows us to check and see all the variety of ways that our UI may look on a user&apos;s device.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:10&lt;/strong&gt; · Even though that colors are accessible by default, this allows you to make sure that your designs still look good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:21&lt;/strong&gt; · Here we can just click a couple different seed colors, and see how it looks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:35&lt;/strong&gt; · The seed color option is really nice to be able to really quickly iterate on these designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:41&lt;/strong&gt; · You can also pick any color in the custom color picker as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:48&lt;/strong&gt; · Our new color system added and removed color tokens, which required mapping across our material components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:55&lt;/strong&gt; · We added the ability of building with a custom theme, and this theme can reflect your brand style and you can set it as a foundation of your app&apos;s UI color Here we can just add a new theme, and switch to it in the dropdown.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:14&lt;/strong&gt; · Moving over we can see that it swapped out the tokens for our branded theme, and now we can choose our brand colors, starting with our primary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:26&lt;/strong&gt; · The primary value input will also create and act as a seed color, updating the other key colors cascading down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:34&lt;/strong&gt; · Now we can choose our secondary and tertiary colors along with our brand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:42&lt;/strong&gt; · Using the Material Theme Builder, this provides a branded color scheme, and make sure that it&apos;s harmonious and accessible by default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:53&lt;/strong&gt; · These still generate the full tonal palettes, which you can use and reference in your designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:05&lt;/strong&gt; · I&apos;ll update the secondary and tertiary inputs with my brand colors, and then i&apos;ll override the key color.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:12&lt;/strong&gt; · This will interpret into tonal palettes, which generate the light and dark scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:18&lt;/strong&gt; · This custom scheme can be used on Android versions 11 or below that do not have dynamic color available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:25&lt;/strong&gt; · If you have semantic colors, they don&apos;t have to be left out, and they will be included as additional colors ready to be implemented.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:33&lt;/strong&gt; · This means that you can use dynamic color for your app&apos;s main theme, and include extended colors for semantics or even brand colors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:42&lt;/strong&gt; · We include in the description, accessibility checks, to make sure that your colors will look good on a variety of different situations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:51&lt;/strong&gt; · You can update the color live in the color picker, and it will update these accessibility ratings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:01&lt;/strong&gt; · Here, we have another example of working with our design kit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:05&lt;/strong&gt; · we can select it and make sure that we are mapped to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:15&lt;/strong&gt; · We suggest keeping the generated colors that you have to ensure flexibility, the styles can be updated to reflect not only in Figma but can be exported next time you open the plugin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:29&lt;/strong&gt; · Building off Figma styles, token and color system can be integrated with custom components and branding styles with ease for cohesive color story and we&apos;ve only painted in the picture for color here, but don&apos;t forget about typography.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:43&lt;/strong&gt; · We highly encourage you to express your brand through type, and Material Design tokens are here to help.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:54&lt;/strong&gt; · A baseline type scale in Roboto is generated along with the color scheme, and they are connected to styles with the appropriate Material Design tokens ready to be customized.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:05&lt;/strong&gt; · I highly recommend anything from Google Fonts, which can easily be implemented in your Android app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:12&lt;/strong&gt; · So let&apos;s see this in action.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:15&lt;/strong&gt; · So one more time, let&apos;s go with the design mocks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:20&lt;/strong&gt; · We&apos;ve now iterated through multiple mocks to visualize how dynamic color will we impact the UI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:26&lt;/strong&gt; · We&apos;ve created a custom theme, and have wonderful semantics of how the colors are used, plus added custom type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:33&lt;/strong&gt; · and they&apos;re all ready to be shared with your development team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:40&lt;/strong&gt; · We wanted to make it easy and allowing you to easily migrate, so we introduced code export.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:48&lt;/strong&gt; · This provides developers an easy access to be able to export the design tokens and integrate it in their apps today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:56&lt;/strong&gt; · This is made to work with our current versions of Material Components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:00&lt;/strong&gt; · So, in conclusion, both dynamic and branded themes with Material You allow you to build beautiful, expressive apps with color that reflects users&apos; choice, and tokens that help the developers and designers collaborate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:12&lt;/strong&gt; · We are so excited to see what you build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:14&lt;/strong&gt; · We will be releasing the Material Theme Builder as an update to the Material Design Kit at this year&apos;s Android Developer Summit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:21&lt;/strong&gt; · Keep following us on Twitter for more information, and follow us @rodydavis and @margeeta. Thank you!&lt;/p&gt;
</content:encoded></item><item><title>Moving an Angular app to Flutter web - DevFest FL 2019</title><link>https://rodydavis.com/videos/talks/moving-an-angular-app-to-flutter-web-devfest-fl-2019/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/moving-an-angular-app-to-flutter-web-devfest-fl-2019/</guid><description>*This presentation outlines the process of migrating an Angular application to Flutter Web, demonstrating how Flutter&apos;s widget-based architecture enables rapid, cross-platform development and feature scaling.</description><pubDate>Mon, 18 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Moving an Angular app to Flutter web - DevFest FL 2019&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;SLT9b7iLisA&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Moving a Angular application to Flutter web in 6 months.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:07&lt;/strong&gt; · so first what is that so that run from a scenario where ways it&apos;s a web component framework [Music]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · [Music]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:31&lt;/strong&gt; · [Music] so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:02&lt;/strong&gt; · now [Music]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:04&lt;/strong&gt; · so we kind of used angular but why in our conception the first person to so we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:22&lt;/strong&gt; · put the information inside processing and generate the HTML so we need our Forks for the second one we need to speed up our process given our development process so we use angular material and it was important one my opinion if you knew how and so we so this was our first&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:06&lt;/strong&gt; · person like a typical application forms [Music] briefing so my body&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:41&lt;/strong&gt; · so after google we got some new requirements for example a prophetic dragon drove support for tablets and smartphones reports and smartphones online users and you might be used of course so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:10&lt;/strong&gt; · so maybe the rack but maybe sounder continuous angler or here with but just included so again flying through the it&apos;s the third packet them arose the theme was present the plug&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:38&lt;/strong&gt; · but one important thing is the new requirements are supported by Twitter so I buy their dragons our support for that lives on smartphones I&apos;ll flank you with this and put their butt&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:09&lt;/strong&gt; · so that&apos;s where I finish so I was working with clutter for like the past two years the pretty involvement community created plug-ins and examples as well as using a lot of repos with water it allows you like Java to create an application extremely quickly scaffold it prototype and demo it way faster than we could with angular so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:34&lt;/strong&gt; · first of all everything in clutter is a widget so that includes the theme the app bar scaffold text even the app itself as a widget so because of that you can create a very it can compose your application with components clutter out of the box has material design which we were already using angular material which made the transition to material design with clutter widgets very easy on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:59&lt;/strong&gt; · top of that it&apos;s a declarative UI framework so you&apos;re writing it exactly as you&apos;re thinking about it or seeing it on a design doc so you have problems and you have literally you&apos;re writing an asthma treatment here and most importantly we wanted a very controller control over a brand and the output of the application we wanted to make sure it looked like us and not just like material design so we came up with our own theme because of that we also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:31&lt;/strong&gt; · needed something with extremely fast welcome we did the first one in a month before Google i/o and we were targeting another beta for dam summit which was also one deadline but yeah so with water you have extremely festival because though it&apos;s powered by that you need to be meaning that you can reach out to anyone on the select channel that we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:53&lt;/strong&gt; · have for github and it&apos;s really easy to connect to people who have them help you and you help them and I think it&apos;s because it&apos;s just kind of a new kind of generation coming up and wanting to collaborate on projects together there&apos;s a ton of plugins on flutter already there was already angular Dart and a lot of the plugins that we used on the web actually came from hanging in the dark but then clutter came along and now they&apos;re wonderful audience too so you get huge taking the tools in flutter it&apos;s a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:23&lt;/strong&gt; · Google product so their tools are really good and the testing has been awesome because you can actually test your entire application without running the application because the player runs the widget tree you can actually test your application you know on a server on Linux without even having to launch our iOS application or Android application so we can&apos;t go through tests really quickly and another really important one is code generation dark has really good&lt;/p&gt;
&lt;h3&gt;Code Generation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;8:50&lt;/strong&gt; · support for the runners where you can specify a minimum amount of code and it generates a lot more for example our database ball it&apos;s about a thousand lines regular but generated it&apos;s over ten thousand and so this saves us so much time be able to do a lot of the boilerplate stuff that would take a little more time so we started looking&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:11&lt;/strong&gt; · the future and say okay well what is our goals for this project like you know we have letter we have something on the web what do we want to do after this and we saw one of the other tools and kind of directions they were doing so we&apos;re saying well if we have web what about mobile or desktop and because of that we needed technology to use that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:32&lt;/strong&gt; · could provide all those to us and very easily so cluttered first and foremost is a multi-platform framework that means that it&apos;s a single codebase one language in multiple octaves you&apos;re not rewriting application for each baktun one you&apos;re writing at once and you&apos;re exporting to Mac they&apos;re exporting to load exporting to iOS and you&apos;re getting a native experience on each one of them they&apos;re even having support for new ones like command line which just added we can actually build a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:59&lt;/strong&gt; · REST API or command line version every application that you already have or even around the REST API in fact I think at Google i/o last year they even ran clutter but a huge like Jumbotron thank you so it&apos;s a anywhere that you can build a runner you can run flutter and we&apos;re currently targeting all these right now except for a big run so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:21&lt;/strong&gt; · flutter just launched web support pretty recently but we have been using it since the time that you weren&apos;t even we&apos;re not supposed to use it we have been on the very material early off of to the point where we had a refactor a project of all times during parts that we&apos;re breaking our application as they went but now it&apos;s a technical preview and it&apos;s a lot more stable but clutter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:45&lt;/strong&gt; · is truly native that means that on each of these runners that you have the platforms it&apos;s painting onto the canvas it&apos;s not running a website it&apos;s not running a virtual machine everything&apos;s compiled ahead of time so that you&apos;re getting insanely good performance 60 FPS on most smartphones and things like I&apos;ve had a second support higher refresh rates flower goes up to 120 is long and they always target that - on the web it&apos;ll go compile your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:12&lt;/strong&gt; · code down to Jas get a PWA application with no effort from you as well as minify code in addition they also launched desktop support so now we&apos;re gonna be launching a Mac OS application let X applications Windows application again we only wrote the code once we&apos;re just making customization for responsive design so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:35&lt;/strong&gt; · another thing I wanted to talk about is working for the team because you know this wasn&apos;t less solo effort we had to work together to get this done in such a short amount of time so because of that because of that a flutter is by nature a very collaborative community so we went&lt;/p&gt;
&lt;h3&gt;Working for the Team&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;11:52&lt;/strong&gt; · to you know a lot of like Stack Overflow yeah we had a lot of issues that we had to go through as even people from the cutter team yeah even people on the cutter team helped us out on different Asian issues which was invaluable as well as teaching each other I told my team kids at the time I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:11&lt;/strong&gt; · was the only one that knew flutter I told them by the time we want to pay that everybody here is gonna know flutter and all they do now because it&apos;s easy to learn and once you learn it you could move extremely quickly and so also through the process of being one of the pioneers on flutter with we also have to start tipping back so we&apos;ve actually open sourced a lot of our plugins that we build including a lot of the firebase authentication on the web the firebase storage and firestorms that are all we&apos;re using with clutter and&lt;/p&gt;
&lt;h3&gt;Why Choose Cross-Platform&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;12:41&lt;/strong&gt; · so why choose cross-platform for startups it may seem like a no-brainer but is definitely important to go through a couple of these points first of all prototyping ideas quickly but flutter is so fast to do an application that your demo can be almost a fully&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:57&lt;/strong&gt; · functional map you can provide prototype ideas in a matter of hours then it takes other platforms to use like a couple of months you don&apos;t have to have separate native teams because I can get really constantly and sometimes be slower but instead you just have a core team and focus on building the core product you have a single code base which means you have a consistent post that means like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:21&lt;/strong&gt; · we fix it one place that fixes it everywhere it&apos;s very weird you&apos;re gonna find bugs in just one platform and what it is it&apos;s going to be something that&apos;s like hated where an SMS plug-in it&apos;s gonna be the iOS that&apos;s messed up most the time all the pure dark stuff&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:37&lt;/strong&gt; · he also spent more time on the core product especially when you&apos;re developing and supporting to not get bogged down and all the boilerplate and getting the project set up and choosing the technology you want to be spending the time focusing on the business that&apos;s going to actually dinner any fun as well as taking that design getting into prototype and getting into products as fast as possible so because of that we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:02&lt;/strong&gt; · also wanted to design for everyone we wanted a tool that could be used by anyone regardless of what device that you think where they&apos;re from because of that we started supporting internationalisation our tool now supports and the switch you can change out the language as well as we&apos;re doing tooltips and accessibility features so if you have a hard time seeing as well as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:30&lt;/strong&gt; · native performance like I said earlier there&apos;s no website run so it&apos;s not like it would be like Cordova it&apos;s not like JavaScript running in the background that&apos;s running compiled to our code machine code running on the CPU directly and each platform looks and feels just right so an iOS you&apos;re gonna have like those specials at the top of it on Android you can have an overcoat on wet they&apos;re gonna have a cover events which you don&apos;t have my mobile at all but the clutter it takes care of all that for you which makes it super nice to use as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:02&lt;/strong&gt; · well as be able to take advantage of existing libraries if you come across something like cocoa pod for example just really easy to add into your application as well as the dll on Windows or JavaScript property we actually found out a relation that creates a lot of JavaScript for the web but still also being able to rewrite a lot of the things in mobile you can use&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:26&lt;/strong&gt; · any existing code the clutter is really easy to add it to your application so let&apos;s say you don&apos;t want to jump all in the clutter because they&apos;ll just do one screen and you can access all your Swift Java JavaScript correctly without having any performance issues clutter eases skia which if you&apos;re not familiar ski it came from chrome so they took the power from without the baggage of the way&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:52&lt;/strong&gt; · as well as you can do custom native or branded experiences what I mean by that is you can make it look like an iOS application iOS and Android application range work but instead we found that it&apos;s better to kind of do your own style because if you if you just build it like a native application we&apos;re just going to think it&apos;s made by Apple were made by before for us we wanted to really bring out our branding and our style into our application as well as with what our&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:20&lt;/strong&gt; · there&apos;s no limits like one of the questions I see all the time is is this possible clutter and the answer is always yes because it&apos;s regular application so that doesn&apos;t mean that it was smooth sailing the whole time we definitely had some performance issues at the beginning with flutter there&apos;s a one a different state management solutions you can choose from since it&apos;s a very new framework and one of the ones we chose at the beginning ended up being super easy at the beginning but perf like us again so we had to change the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:52&lt;/strong&gt; · one we were using and into the working a whole lot better we also switch to web assembly so you see what people like in the browser and run billions of queries instantly so that dramatically improved their application as well as being able to adapt to change quickly since we were in early adopter clutter on the web we had it we had that we knew that it was going to break you just winced hope we had to be willing to jump over these&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:18&lt;/strong&gt; · hurdles as they came because of that it made us very intimate to call the limitations and the pros that I had as well as the things that we can improve white art a lot of people that&apos;s like one of the first things to ask why would you just start clutter like why not caught that why not Swift and because of that dartie has also changed a lot too so we&apos;re now on dart - it&apos;s a strongly type of language it&apos;s not like the first generation of dart was replacing JavaScript and the reason people you start is because it runs in two&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:49&lt;/strong&gt; · different modes dark that&apos;s a IOT compiler and a JIT compiler so when you&apos;re coding you&apos;re using the typical pottery and make it change it gets hot reloaded right into your your code that&apos;s running with your state so you can be twenty screens in your application they can change and it updates that screen without having to start your application on the way over but then when you release both because&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:11&lt;/strong&gt; · you have a little compiled arm code complication I promise extremely fast as well all the time on the web you have minified version of javascript versus a ad debuggable version of javascript there&apos;s also really good tools and testing like I said earlier dark has a built-in testing clock marbles the order integration tests and unit tests and it&apos;s also pretty easy to learn there&apos;s a slight learning curve if you&apos;re coming from JavaScript but if you&apos;re coming from Swift or confident you won&apos;t even notice it&lt;/p&gt;
&lt;h3&gt;Tools and Testing&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;18:41&lt;/strong&gt; · especially so it&apos;s important to say that we did this in six months from the start to where we right now and by doing that we had to learn about clutter develop a path forward and develop the application&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:01&lt;/strong&gt; · well there&apos;s only great because of the tools the community and a language of mind there they&apos;re good on their own but they become really powerful when they get it together there&apos;s really really good open source projects at the moment there&apos;s lots of plugins into your dart and flutter as well as there&apos;s like I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:18&lt;/strong&gt; · said earlier a lot of community health and collaboration good tools and debugging as well as like we just made just launched the dart extension switch for a very welcome feature as well as Golden Pond testing so you can literally screenshot your application running unit tests to make sure that the UI looks exactly like the screenshot very powerful for preventing regression folks and it&apos;s also easy to understand there&apos;s even flutter has been getting popular in China I&apos;ve been able to read through their source code pretty easily because of how straightforward it is so even if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:50&lt;/strong&gt; · there&apos;s not a lot of comments so some other technologies that we&apos;re using in this application are webassembly sequel life microservices and king of actions we actually fully automate the deployment and testing application we get out of actions which makes working together very nice as well as micro services or even broke apart like different parts of our application and we run it independently which makes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:20&lt;/strong&gt; · it nice for abstractions so one of the things you always see when you building an application if you&apos;re trying to go native is what about Android what about whether about iOS because of that you have to choose a platform that lets you build to all these weapons at once send updates simultaneous&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:43&lt;/strong&gt; · so by doing that you get to focus like I said earlier on the core product you get to focus on building what matters and what&apos;s going to generate you money in the long run so flutter doesn&apos;t have CSS or HTML it&apos;s not a wet language that you&apos;re using on mobile it brings designs to life because you are writing it declaratively you get to bring all your features to the market faster since you have told us about testing as well as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:14&lt;/strong&gt; · being able to build for all platforms and people there&apos;s less overhead by having a smaller team which is focusing on flutter and dart in fact you can even split out the backend team from the front of team the people to the other people on our team they do just the you are going to have to do the logger on the backend but work together on the same project together and you have faster updates by&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:40&lt;/strong&gt; · not having multiple platforms that you&apos;re having to develop or individually it&apos;s you can just make impact and then it&apos;s update updated forever as well as we use a lot of Google products and because of that this application is built on top of a cloud run or using dart code we actually have a full REST API Google in dart as well as we use firebase apart service architecture and technologies for the exporting flutter or the UI and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:13&lt;/strong&gt; · everything else in region G suite so it can even pepper over or DSU&apos;s together stainless and outside just really trying to cooperate that way because it&apos;s made the process a lot easier so hitting here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:31&lt;/strong&gt; · like reaching beta is such a huge milestone and very important because of that we had to go through multiple iterations this you know like the first version that you&apos;re gonna build is not going to be the last person version that you deploy it&apos;s important to realize that because there are things like just by nature going to figure out along with that and maybe worked when you&apos;re prototyping it that don&apos;t work want to get to the release so what we did is we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:58&lt;/strong&gt; · rebuilt it like about a month ago with better UI and UX and decided on a way that really makes sense and trying not to be stuck in a sunk cost fallacy we wanted to say what&apos;s going to make the product great how can we make it go to what things can we do to make it better so part of that you just have to be able&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:20&lt;/strong&gt; · to move fast and break things fix them as you go because if you build an application that&apos;s bug free it may be good but the UX might suffer too because you have to get beta feedback to know how it&apos;s actually working so trying to say that it&apos;s available today if you want to go to the editor it&apos;s Amster web app and if you want to see more information about amps or itself it&apos;s stored on Co the entire application is written in flutter for the web it&apos;s a full colon editor you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:52&lt;/strong&gt; · export perfect age to the 5-amp HTML yeah AM code and as well as we post footballs too for you as well so in a matter of two minutes and breathing am story and it just share a link which I handle all the posting for you as well as we kind of made that editor look like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:12&lt;/strong&gt; · sketch in vs Cohutta babies we want to do and target both audiences because we know that they&apos;re gonna be developers in in school and designers as well so because of that we wanted to have the experience that worked good for everyone and just wanted to say thank you for [Applause]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:03&lt;/strong&gt; · because for us we wanted to be able to have like a desktop application which is really big so by doing that we started with mobile first and we wanted to say okay well let&apos;s do it on desktop I&apos;ll probably do it on my desktop we had to do a huge responsive design and when flutterin announced technical preview it was really easy for us just to launch the website where because there&apos;s no extra work&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:30&lt;/strong&gt; · so we are now like when we were doing that of course and then when flutter and now something will preview we now had support for West and we were able to do it was just it took a little bit of I think couple jumps for libraries for someone guess that we needed but other like we had a whole web application in just a matter of weeks since you&apos;re on our team yes so I am&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:05&lt;/strong&gt; · I was I was at the flutter hackathon and working there but they these guys were the ones that were call putting it on they found me and then I do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:44&lt;/strong&gt; · we are both from Macedonia we came here in the States I was working remotely for a contractor and that&apos;s how much we child but really we actually attended organized as DDT&apos;s we organized this cluttered hackathon and Rudy was one of the speakers there and that&apos;s how we met with him and he gave us an amazing idea that basically change the entire application and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:27&lt;/strong&gt; · [Applause] it is still a bit after tune you can find a lot of parts they&apos;re using flutter we made the app so quickly it&apos;s it is a huge light app it is a editor where you can basically build a fit page with just check it out and it&apos;s free&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:22&lt;/strong&gt; · [Music] for startups and then you&apos;re building your startup online and then if you need some support and storage like you could show some really interesting direction or yeah when we say okay we can provide you with so in our case&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:57&lt;/strong&gt; · [Music] because it&apos;s important that we were able&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:39&lt;/strong&gt; · to focus on the product itself and not having to worry about you know making money I can at first like month or two because of that I think it really speaks in the application and be able to not having to be bound by it because you know we&apos;re we&apos;re doing this right now like it&apos;s really good to be able to just build a product that you want to make&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:43&lt;/strong&gt; · so with us like I&apos;m working on marketing and then roubini is the mastering fire rate but the job started in England now she&apos;s a master in the clutter we&apos;re learning to code in butter and like that is how our team works like we&apos;re supporting each other someone the song at the time&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:13&lt;/strong&gt; · but then I&apos;m stepping up and I&apos;m going to learn to do this so like a big part of it was the food and that&apos;s why&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:52&lt;/strong&gt; · [Music]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:17&lt;/strong&gt; · yeah the only thing we had a change was like some gestures and adding some different UI like gestures for mobile things like hover events things like you know things that didn&apos;t exist on mobile but after that it was because we were already worked in a desktop application we had to do with 90% of that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:37&lt;/strong&gt; · yeah so when you do wonder create you get iOS Android and web for your default applications you can literally copy into the desktop folders and you have desktop whole web one project and then even after the command-line like it&apos;s it&apos;s literally just adding a folder because the core logic is inside the lip floor from dark so you don&apos;t have to keep building these different projects and it&apos;s like it&apos;s all just running off the same code&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:12&lt;/strong&gt; · yeah you can&apos;t do like it platformers iOS but the a lot of the core stuff is handled independently on the platforms already from the fighter team so it&apos;s going to have Iowa&apos;s styling and gestures on iOS and the reverse on the Android so you can have a higher distraction or you can keep going down lower another huge thing about flutter is the entire thing all the way down to the right above the ski layer is already dark so if you want to build your own painting library you can just extend it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:44&lt;/strong&gt; · and then write your own if you want to bundle your own bird in a flutter and camp there was lots of things where sometimes we&apos;d have to go down to a lower level make a change make a patch or even just like add features that didn&apos;t exist but because of that it was super easy because you&apos;re having the same language it was also a really nice way to have documentation so if there wasn&apos;t an example on how to do something you could always go to the definition of the thing inside the engine and keep going down it down&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:21&lt;/strong&gt; · nope because we&apos;re using Google clock run and part rate so saying if we have the million years as tomorrow our application will be charted across multiple servers and with firebase and flat document structure there&apos;s no more stuff that we have to do in our site so but we built it with scaling in mind at the beginning what are we going to do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:53&lt;/strong&gt; · like&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:18&lt;/strong&gt; · the UI for mobile because right now it is design okay we&apos;re done yes you can use it on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:45&lt;/strong&gt; · [Applause] [Music]&lt;/p&gt;
</content:encoded></item><item><title>Road to Flutter City - Rody Davis</title><link>https://rodydavis.com/videos/talks/road-to-flutter-city-rody-davis/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/road-to-flutter-city-rody-davis/</guid><description>This presentation introduces Flutter, a high-performance, cross-platform SDK that enables the creation of pixel-perfect UIs for mobile, web, and desktop, demonstrating its capabilities through a custom Flutter Piano application.</description><pubDate>Thu, 20 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Road to Flutter City - Rody Davis&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;N1PKhvyDYyg&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Source: https://github.com/rodydavis/flutter_piano&lt;/p&gt;
&lt;p&gt;Slides: https://docs.google.com/presentation/d/18FuD56CHamy9jIi66xjvzb-68QA4e3Kr0xIjHRvLKMs/edit?usp=sharing&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Flutter Piano&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · okay perfect awesome all right well yeah welcome everybody to road to flutter city today I&apos;m gonna be talking about flutter piano I am my name is rody Davis and I have been using flutter since flutter beta-2 so we use it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:19&lt;/strong&gt; · professionally on a company called cross-media where we build mortgage apps for CRN&apos;s and we very aggressively used flutter and actually a couple apps that we have in addition I also make a lot of my apps on my own when flutter create came out I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · thought it was a really cool idea and the constraints so I decided to build a piano out so today I&apos;m gonna kind of walk you through a little bit about flutter and my process as well as an example of the flutter piano alright so&lt;/p&gt;
&lt;h3&gt;What is Flutter&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:58&lt;/strong&gt; · first of all what is flutter well flutter is a like we talked about earlier it&apos;s um a mobile first SDK that can run on desktop as well as web now it has native performance because it compiles down to machine code unlike react native which runs in a virtual machine you get some really high quality animations as well as pixel perfect UI because flutter draws everything on the screen you&apos;re not having to worry about building XML files or even Swift using&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:28&lt;/strong&gt; · the native components you can really build a brand exactly like you want for whatever client or company that you want to so here&apos;s an example of the stateless a stateful widget so anything within&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45&lt;/strong&gt; · this class called state and because of that it&apos;s actually able to hold its own values now to rebuild the widget tree you have to manually call set state and in contrast this is a stateless widget which does not extend state and it just returns whatever is given to it so an&lt;/p&gt;
&lt;h3&gt;Stateless Widget&lt;/h3&gt;
&lt;h3&gt;Stateful Widget&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:08&lt;/strong&gt; · example of a stateful widget would be if you want to have like I like the the demo application is a counter application every time that you want to increase the counter by one you need a call set state and that increases a variable now and stateless widget you would use that for if you&apos;re composing your widget tree of smaller widgets you&apos;d have the stateless widget that just passes through whatever it has all right so when you&apos;re getting&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:38&lt;/strong&gt; · first started with flutter it can be really daunting because of all the widgets everything is a widget and flutter including the material app including down to the button as well as individual properties and the themes so once you get started it&apos;s a lot easier but definitely like the first couple weeks and the flutter are gonna be overwhelming in that in that sense one&lt;/p&gt;
&lt;h3&gt;State Management&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:03&lt;/strong&gt; · of the things that flutter provides that a lot of other SDKs don&apos;t is the flexibility of whatever state management tool you want to use now there&apos;s a lot of different options out there currently block is one that&apos;s used well this is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:19&lt;/strong&gt; · used between the angular dart and flutter shared platform that they can you hear a lot of the business logic code there&apos;s redux which was actually made by Facebook and it&apos;s a it&apos;s a one-way relationship of data so the data comes in to the model and then it outputs to the view there&apos;s scoped model which is very popular it came from the future platform which is a a tool that allows platform&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:49&lt;/strong&gt; · they could have been working on and more recently and the flutter team has come up with provider which is the version of scope but it allows you to do dependency injection down into any part of your app there&apos;s mob X which is a a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:06&lt;/strong&gt; · similar to redux there&apos;s rx start as well as you can just choose vanilla set state but we&apos;ll see later how important that is to look at other tools because you&apos;re gonna have to keep passing down and passing back up data you can also completely build your own custom state management if you wanted to so you really do have 100% freedom here&apos;s an example of calling set state so here you have your value you have your your method for calling sex and&lt;/p&gt;
&lt;h3&gt;Set State&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:38&lt;/strong&gt; · then you can present that value itself every time you do something you have to call set state otherwise it won&apos;t rebuild your widget tree and this is what you get out of the box now one really cool thing about flutter is the community flutter has been able to has&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:58&lt;/strong&gt; · been getting really popular and so many people have been putting packages into flutter there&apos;s a really good chance that if you&apos;re looking for something it is already done on footer and and it&apos;s already released on dart and dart pub that&apos;s what we call it and what&apos;s cool about that is you don&apos;t have to reinvent the wheel every time and you can every almost everything on dart pub is open source so you can just go right to the code so let&apos;s just do an example real quick so I can go to dart pub&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:40&lt;/strong&gt; · there we go all right so let&apos;s search for a calendar with it so we have a couple different options you can even go down and view the dependencies itself this will have what you need to set up on iOS and Android almost every one of&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:58&lt;/strong&gt; · these included an example as well you if you&apos;re curious you can always tap on the repository you can go to the github and you can check the issues to see if it&apos;ll work well this is also how you can manually override dependencies from github if you have if there&apos;s an issue like a while back Android X was a really big deal so yeah so I would say before&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:23&lt;/strong&gt; · you try to make a custom widget I would try to look at dark pub first because there&apos;s a good chance like I said that it&apos;s already done so so yeah so we had a flutter create contest that just recently finished and one of the biggest things about that was you had to build an app with like less than I&apos;ve kilobytes worth of code for reference that&apos;s like less than 180 lines of code which is pretty difficult and you can&apos;t&lt;/p&gt;
&lt;h3&gt;Flitter Create Contest&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;6:52&lt;/strong&gt; · can&apos;t use the majority of your code can&apos;t come from plugins the code has to be graded on different criteria so there was code quality beauty novelty overall execution and there was a grand prize overall there&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:11&lt;/strong&gt; · also a limited time line but what made that nice is that there were just simple rules so you could just like focus on building and not have to be worrying about like building a full app you&apos;re just working worrying about this small little project so with 200 lines of code there&apos;s definitely a lot of design constraints that you have it also has to be open source and to get into as much&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:37&lt;/strong&gt; · code as possible it really mattered about minifying it so that means that it didn&apos;t look pretty but it was as condensed as possible so for mine I came up with the idea to do the piano because I was a musician growing up and that&apos;s a huge part of my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:56&lt;/strong&gt; · life and I wanted to bring in that to development I&apos;m also the one that came up with the MIDI plugin for flutter so it allows you to with a very simple API be able to play MIDI on Android and iOS and also it&apos;s something that you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:12&lt;/strong&gt; · generate a piano has a series of octaves and that they they only change minimal values so I figured I could build something out like that and just for a frame of reference I built this entire flutter piano app in less than eight hours and I released it within 24 hours of the contest opening so after that I&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:35&lt;/strong&gt; · got to work I got to work with the community and I decided to make the codelab I&apos;ll have links to all this on my github afterwards but you can go through the entire my step tutorial that I had online as well as I made a I&apos;ve been making some YouTube&apos;s about YouTube videos about it as well also you can go down to the App Store on Android and iOS and you can see that so I also have this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:05&lt;/strong&gt; · link too so you can actually download it right now on your phone and play with it if you want to I released it free for I think this month and it&apos;s actually got over 16,000 downloads in just a couple of days so it&apos;s a really cool way to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:21&lt;/strong&gt; · give back to the community and and being able to see everybody use it so and I would also like to just point out that I&apos;m also using code magic to deploy all my apps in the cloud and if you&apos;re not familiar code magic is a tool that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:40&lt;/strong&gt; · allows you that&apos;s built for flutter that allows you to release your apps on iOS and Android right from your home screen so if you look in here you can see you can have your app you can deploy your builds and it like it has it made for flutter so you can go in and choose all of your specific flutter setting and you can build for ios and android as well as choose your actual actual Flender channel and there you go alright&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:19&lt;/strong&gt; · so now I&apos;m going to just kind of show you around the flutter piano app and there we go alright so first of all this so let&apos;s start with the pub spec taught you Hamill so in every flutter project&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:39&lt;/strong&gt; · you&apos;re gonna have this it&apos;s gonna have like your basic app settings this is where you manage your dependencies for what your app is also we can include information about who built the app where it&apos;s located and what version with the later versions of flutter you can now include a version number that when&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:59&lt;/strong&gt; · you build your app it&apos;ll actually build the iOS and the Android side so you can do auto incrementing if you want to it&apos;s really helpful because before you had to manually go into each project and do that this is also where you specify your assets so for this example I have all my&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:15&lt;/strong&gt; · assets in a top-level folder I can include those here and then link to them here in the pub spec you you can use optionally new material design you don&apos;t have to it&apos;s on by default but you could actually run your own complete design theme if you wanted to in a lot of my apps I do on the iOS it looks like iOS and on Android it looks like material design and it usually includes this Cupertino icons package as well so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:46&lt;/strong&gt; · here&apos;s the flutter MIDI plugin I built and then I&apos;m also relying on tonic which is a music plugin that just has all the basic stuff like scales and pitches and just the convergence so if you look this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:03&lt;/strong&gt; · entire app is a hundred and forty three lines of code it came out to actually be fifty one thousand two hundred bytes or yeah five zero to four and here&apos;s the app running so as you can see really fast and responsive it has the full keyboard and you can play as many notes as you want to you can also change the key width and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:37&lt;/strong&gt; · you can also toggle the labels on and off perfect so as you can see let&apos;s go through this building method so you&apos;re gonna every app every flutter and dart application even the ones from the command line all have this main method this is where your app entry point is here we call our first widget which is my app this my app is a stateful widget and what it does is as soon as the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:08&lt;/strong&gt; · widget starts I have to load the the asset bundle this is the the root the root asset bundle that flutter provides this is a good example if if you ever want to load something that&apos;s not a image or text this is how you can load any file so this is just an arbitrary dot sf2 file all the sound fonts and I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:29&lt;/strong&gt; · actually able to load any file type I want in the flutter so what I do is I take that I load that flight data and I pass it right down into flutter MIDI prepare so now that the the MIDI is up and running I have to define a couple variables so I&apos;m gonna specify the key width because this is dynamic I want it to be based off of an 80 point width and it can change the percentage based on whatever the sliders are which is this width ratio as well as the boolean to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:00&lt;/strong&gt; · show and hide the labels so here we start with our material app you can use either material app or cupertino app for your application in this example I went with material you can choose your your theme so I went with a dark theme and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:19&lt;/strong&gt; · for the home I have a scaffold the scaffold is the thing that provides you with basic things like an ability to add an app bar and a floating action button or a bottom tab bar all these things you get out of scaffold for free and you can customize them any way you want you can even build your own&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:41&lt;/strong&gt; · if you wanted to you can extend it so here we have the the menu this is the thing right here that is going to have our variables so we have an instance called dwarves which does about the intake and this handle is what is nice about flutter is this handles all the the elevation so if you see it actually hides the background right here or it shows a scrim color and this is really&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:05&lt;/strong&gt; · handy for just out-of-the-box simple declarative UI I here I add a little bit padding here&apos;s a slider so this is an example of calling set State on a change so slider itself is actually a stateless widget and it has to tell you every time that the value underneath it changes and then when I&apos;m doing that I&apos;m calling set state which recalls this build method and then that build method rebuilds a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15:35&lt;/strong&gt; · slider with a new value if you didn&apos;t use that state you wouldn&apos;t be able to move the slider back and forth here&apos;s a divider just to make it a little bit more readable and here&apos;s an example of a boolean now something you can do with the more recent versions of flutter is you can have this thing called adaptive so if you change that now when it reloads it&apos;ll actually look like the iOS icon and if you take that away it&apos;s going to look like the material so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:08&lt;/strong&gt; · that&apos;s a a new thing and flutter that makes it really easy to or to reduce the amount of code that you&apos;re doing on iOS and Android not everything supports this but there&apos;s more and more widgets that are coming out that do that this is also our app&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:27&lt;/strong&gt; · bar that includes our title our center and here&apos;s where the real magic happens so we take in the ListView a ListView can have them multiple different modes there&apos;s the you can just pass in a list of objects which is called a children or you can use this builder method which is because all you have to do is specify account and then it&apos;ll call this item&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16:49&lt;/strong&gt; · builder for every single item in that list you would use this if you either don&apos;t know how many items are in the list or if you&apos;re you&apos;re going through just thousands and thousands of items you want to because this will handle like if you guys are familiar with recycle view or the table view controller on iOS this handle is reloading and only showing the widget center on the screen at any given point&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:13&lt;/strong&gt; · here I can specify a scroll controller so this is the thing that executing manage the ListView you can update this independently and I&apos;m just saying initial scroll offset which will load it to middle C otherwise if you don&apos;t have the scroll controller it will just start at the beginning so if I do this if I comment it out and hit restart and this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:37&lt;/strong&gt; · is an example of hot restart and flutter which actually restarts from the very beginning you&apos;ll see that it goes to the very on C one and if we take this back and restart again it&apos;ll load right to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17:57&lt;/strong&gt; · middle C and if you want to just update the changes in your app you can use hot reload but anytime that your build method changes or your main method or anything that&apos;s part of your state you need to use hot restart if we go down&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:13&lt;/strong&gt; · here this is specific just at OU and we&apos;re also specifying your skull directions so by default list view scroll vertically but I&apos;m using this list view to scroll horizontally and you just pass that with the simple access parameter I&apos;m getting the index so in this case I want to find the octave and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:36&lt;/strong&gt; · so I&apos;m rebuilding this this section for every single octave in the piano so here&apos;s just an example of one octave you&apos;ll see that it has a stack a stack is a way for you to build two widgets on top of each other and position them accordingly so I have a row which takes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18:55&lt;/strong&gt; · up the entire width of the stack and the entire space and I have another position that takes up just you know 75% of that space giving out the top so here this bottom parameter it&apos;s giving that padding so this Y area each one of these I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:15&lt;/strong&gt; · actually passing in the middie value so this would be 24 plus whatever I is times 12 and then I&apos;m saying if it&apos;s accidental or not so the accidentals are up here the black keys at the top and also like we talked about earlier like the flexible widget on the video that we showed this will fill up the space as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19:39&lt;/strong&gt; · much as possible so in this case I&apos;m using space between which will try to fit make all the keys the correct width on the outsides I&apos;m dividing the widths by two and then I&apos;m adding a key in the middle so that gives you like there&apos;s an empty key right here as well as there&apos;s half key right here and here what you don&apos;t see so once we have that method we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:06&lt;/strong&gt; · can really with flutter it makes it really easy to reuse code so I&apos;m not actually creating eight different keys I&apos;m just calling this one build key stop once and I&apos;m doing the changes that I need to so here I&apos;m using that package called tonic and I&apos;m getting the pitch from the MIDI number I&apos;m able to get the name of the key here we specify another stack semantics this is for if you have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:33&lt;/strong&gt; · screen readers or if you have some customers that have a hard time reading this is a really easy way for them to be able to know what you&apos;re trying to do so in this instance I&apos;m passing the pitch name because the MIDI note is not going to make any sense for if they&apos;re reading through the the app and not able to see&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20:53&lt;/strong&gt; · so I can also specify it&apos;s a button so that they know that they can click on it here we have our material widget which can add a border radius border radius as well as like an elevation so in this instance I&apos;m checking to see if it&apos;s an accidental I am making the color black or white accordingly here&apos;s an inkwell this is the thing that manages the actual tapping itself so if you see that how it goes gray this is actually called an ink splash which is from an inkwell same for this bottom key&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:24&lt;/strong&gt; · when you tap on it it will it this does it for you out of the box you can change the color at any time with the ink splash property ink color and here what I&apos;m doing is I&apos;m playing the note as soon as tap down is fired and here we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21:43&lt;/strong&gt; · have another position widget that actually is just up from the bottom by 20 pixels and that&apos;s going to if show lub show labels is true it will show the text of the label itself now once I have this entire piano key I I&apos;m reducing that so I can check to see if it&apos;s an accidental I want to give it an elevation of 6 because if you see there is a light shadow on the key itself but&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:12&lt;/strong&gt; · there&apos;s not a shadow it&apos;s here at the bottom and that&apos;s because of this elevation property we can change this to whatever we want so if I did 24 you should see that it gets a lot darker but and if you get closer so let&apos;s do 5 the shadows could should get more condensed just like that so the elevation is really useful a lot of properties take that so like even the app bar for example right here if I type&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22:45&lt;/strong&gt; · elevation and I give it like 24 we&apos;re going to throw up a shadow right there perfect and if we go down once we have this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:01&lt;/strong&gt; · we&apos;re returning the container we&apos;re going to if there if it is an accidental we add the elevation otherwise we just returned the piano key and finally at the very bottom we can specify our border radius that&apos;s so everything in the app matches I&apos;m only doing a border radius on the bottom so here you can specify bottom left and bottom right so another thing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:24&lt;/strong&gt; · that I did to get around this this code limit is I had to because my app works only in landscape I could not I didn&apos;t have enough code to be able to do that manually on or inside this building so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23:43&lt;/strong&gt; · what you have to do if if you ever in that scenario and you don&apos;t want to do it from code the nice part about flutter is you can still manage that from you can still manage all your settings because you get a native ios and android app so in this example let&apos;s open up Xcode alright and in this scenario you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:15&lt;/strong&gt; · can I specify that it&apos;s landscape and landscape right so you&apos;re getting a full iOS application as well as Android application you can you can add watch apps and you can do whatever you would normally do with a regular app so definitely don&apos;t feel limited by what you can do and then just finally here&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24:49&lt;/strong&gt; · some places where you can find me I am on twitter as roadie davis as well as github by apple educate you can also go to my website at rody davis calm and you can find me on instagram this is the e let me show you the URL real quick&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:09&lt;/strong&gt; · and I&apos;ll have links for this and I&apos;ll provide links for you guys but if you go to Apple educate flutter piano this will have all the necessary stuff that you need to get started specifically if you want to see what we went over today if you go to the 5k branch this is what I actually entered to the flutter contest itself now guys are also curious this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25:33&lt;/strong&gt; · master branch which I&apos;ve actually been adding a ton of stuff so you can see a more complex application for me not limited by the size constraint but it&apos;s still really small so thank you so much guys I hope that this conference goes really good sound yes it can make sound but&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;26:22&lt;/strong&gt; · there&apos;s a limitation of the iOS simulator so I can&apos;t produce it but if if you play it on a real device like an iPhone or Android it&apos;ll actually it&apos;ll play you just won five are you 200&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26:42&lt;/strong&gt; · minimum about 80 fence sounds okay so are you asking how I can play the sounds from flutter okay so in this example I&apos;m passing a a sound font and what does sound fun and sound font is is it&apos;s basically like a MIDI engine so in flutter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:14&lt;/strong&gt; · and also if you guys are curious these are all the plugins that I&apos;ve made for footer so definitely go and check some of those out if you want to see Warren death different things that I&apos;ve done so if we go to the flutter MIDI you&apos;ll have a this is what I&apos;m actually calling so if you see I call flutter flutter MIDI dot prepare and that is right here I&apos;m&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27:41&lt;/strong&gt; · passing the sound font and setting up the channel see it calls channel invoke method and what prepare MIDI does is it sets up the iOS and Android MIDI here we can I put do the play MIDI note and it calls invoke channel playing MIDI note so what that does is for every platform that you have a platform channel it will call the iOS and Android code so it&apos;ll call Swift and it&apos;ll also call Java on Android so here you have that same exact&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:14&lt;/strong&gt; · channel here&apos;s it in Swift you have your Swift MIDI plug-in here&apos;s the method call for what I&apos;m calling here is the prepare MIDI that we just talked about and then when I do play MIDI note here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28:30&lt;/strong&gt; · it&apos;s actually taking my audio synthesizer and actually playing it so um yeah with flutter you can use native code just like you would in dart code so you really can you can as much as you want alright thank you you&apos;re welcome ok yes yeah this entire app runs offline&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:14&lt;/strong&gt; · okay that&apos;s right okay I&apos;m ready it has it&apos;s a possible so I am coming&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29:32&lt;/strong&gt; · MVP MVP ham of that person in the right team both applications yes you want sorry say your question more time here with you you need speed enough occasion it was not akiza so what happened was bit so let me show you an example of a application that I&apos;m working on okay so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:11&lt;/strong&gt; · there&apos;s a lot of different methodologies you can use in flutter I&apos;m becoming a huge fan of very close to MVP but what it allows you to do is you take pretty much have a model for every version of your screen so here let me show you the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:29&lt;/strong&gt; · basic folder structure real quick so inside your lib I usually have three folders I call data UI and utilities here&apos;s my main method and my constants that don&apos;t change inside my utilities I can include all my shared functions that are going to be global across my app&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30:50&lt;/strong&gt; · inside the data folder I usually contain like some top-level models and then I have my global models inside each one of these models contains just pure business logic so that means that there&apos;s no UI all it is is just taking and modifying code so you take that model and you pass it down let&apos;s go to sorry right here so you can have your&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:21&lt;/strong&gt; · models take you take your model and then you pass it down your widget tree so now if in any screen so right here in any screen I can look up that model and I pass it down so it&apos;s a very much of a a Model View and then controller setup&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31:45&lt;/strong&gt; · anytime that the view wants to update something it sends it to the model and then the model updates the screen the screen doesn&apos;t contain any business logic so it&apos;s literally just a front-end for your model so I was good as the one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:10&lt;/strong&gt; · used this money plates Illidan sue Taniya how you do stuff provide us with those that states what&apos;s no use makes you choose certain types of money okay so one of the reasons why let me give you an example of why you would want to use not use set state so in this example of the flutter piano I have my drawer&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32:42&lt;/strong&gt; · and anytime I want to change is item in that drawer I have to call set state in my variables here but in this app I have I have a separate class called app drawer now this thing has things that change itself but if I were to call you set state I would have to have a non change property of the drawer and keep passing up all the changes so every time that you have a nested widget you&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:11&lt;/strong&gt; · going to have to keep providing that value straight up so in this instance I would have to call on value changed and then I don&apos;t have to go back up the widget tree and then send back but in this method when you&apos;re able to use provider this handles looking up&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:30&lt;/strong&gt; · regardless of where you put this widget it&apos;ll handle all the rebuilding for you another big reason why you want to do that is this allows you to refactor your app really easily so I can take this and move it anywhere in my application I could rename this if I wanted to and I can also break it up&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33:51&lt;/strong&gt; · into smaller widgets if you&apos;re using set state by default you have to do a lot of a lot of extra boilerplate just to get up and running with passing the data through and then plus the widget tree itself you can have one section over&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:08&lt;/strong&gt; · here on your app that has like let&apos;s say your login screen and then you have a home widget that handles like your authentication like it like your account info but let&apos;s say you&apos;re away over here and you have a list view well if this data changes and it updates your account you have to go all the way back up to the top of your app to rebuild just this one part so with a lot of other statement&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34:32&lt;/strong&gt; · providers it allows you to in any part of your application and be able to rebuild your widget tree from the ground up and not only that it&apos;ll actually only build that one little widget that needs to be rebuilt it doesn&apos;t have to rebuild your entire app every time inputs as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:00&lt;/strong&gt; · let&apos;s see that&apos;s constants for example and we have about 20 lines of code with different custom variables and they only need again and then I get into my activity right into my cloud app page and I only need one constant for that box philosopher and that means that Universal is to import the entire data doing that only need one value I chopped&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:32&lt;/strong&gt; · up a flying fox so am I was thinking average impulse several in snuffed out my into my page doesn&apos;t happen anything to with performance does have any oh yes oh yeah so what what I usually do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35:53&lt;/strong&gt; · is I in every directory of my foot or application I include an index start the index up dart takes care of the exports so if you do in dart if you do export you can actually have multiple dart files associated with that index and it&apos;s actually not it&apos;s very performant&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36:11&lt;/strong&gt; · because flutter uses something called tree shaking so that in debug you&apos;re able to have access to everything but when you actually build your app for a release mode it&apos;ll go through and delete all those unnecessary imports so that your app is as minimal as possible so you don&apos;t as a developer don&apos;t have to worry about importing something that you&apos;re not using as well as so many imports so when you actually compile it for release it that&apos;ll be taken care for you are you asking what languages you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:12&lt;/strong&gt; · need to learn footer yeah I would say for footer are you asking what things you want to learn about dart like what things you need to yeah do I need one oh&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37:44&lt;/strong&gt; · I see okay I actually came into flutter with zero years of experience I I was an iOS developer and Android developer before I got into flutter so I knew Java and as well as but I actually went even from the very&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:03&lt;/strong&gt; · first beta when there wasn&apos;t a lot of documentation or there was documentation there wasn&apos;t a lot of like community articles and stuff about it you can actually pick up Dart really easy and I had no dart experience but I was still able to build our our app at our company in two and a half months just from learning Dart you know as I needed it it&apos;s a very nice language and it&apos;s built for the developer it tries to make it as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:30&lt;/strong&gt; · easy as possible to do things it&apos;s also really similar to C sharp so I was pretty used to that as well as Java it&apos;s very close to Java but yeah I would say yeah you can definitely learn Dart you need to I would say its easiest to find an app that you can use to kind of learn it but yeah the documentation and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38:51&lt;/strong&gt; · videos and there&apos;s a lot of code labs that are really good about kind of walking through for me the biggest thing that actually helped me was if you go to the flutter gallery app that they provide it shows you a demo of how to use all the widgets that they give you out of the box so that was really helpful for me to be able to see exactly how to use a widget when to use a widget and how to make it look good okay thank you so much you&apos;re so welcome thinking yes welcome thank you yeah all right&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39:33&lt;/strong&gt; · okay so&lt;/p&gt;
</content:encoded></item><item><title>Space Apps Orlando 2019 - Space Curiosity</title><link>https://rodydavis.com/videos/talks/space-apps-orlando-2019-space-curiosity/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/space-apps-orlando-2019-space-curiosity/</guid><description>Space Curiosity is a cross-platform mobile application built with Flutter that utilizes APIs from NASA and SpaceX to allow users to explore global space facts, track the ISS, and calculate planetary weights.</description><pubDate>Sat, 19 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Space Apps Orlando 2019 - Space Curiosity&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;InYfMPVQ4Wo&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Building a global, cross platform application with Flutter&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · Krupke well wrist api&apos;s and to your pc but today i want to talk about one of the apps that I&apos;ve been working on is the basic curiosity and this app is actually available currently right now on Android and iOS so please feel free to download it it&apos;s free and the goal of this app was to be able to leverage all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:21&lt;/strong&gt; · powerful space api&apos;s that are out there at the time working on this we did not see anything that was just quite like this so I wanted to create something that anybody could download and be able to come and explore so some port it has&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:38&lt;/strong&gt; · an interesting origin - this is actually a collaboration between two open source projects daily NASA and SpaceX ago I&apos;m not sure any of you guys have heard of SpaceX go but this was actually one of the foundation points that we brought in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:55&lt;/strong&gt; · I met with the developer reddit we talked about the possibility of collaboration and we decided that would actually be able to benefit by both working together because at the time clutter had just come out and I had a background in iOS he had a background in Android but we both had a background we were both starting flutter so this is a good opportunity for us both to learn so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:19&lt;/strong&gt; · when we started working we wanted to plan out what we&apos;re going to do for the project so we decided we&apos;re gonna bring in all of SpaceX ago which includes using the SpaceX prep QL API as well as the NASA images API and other various ones as well as a ISS tracker a weight&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:40&lt;/strong&gt; · calculator so you can calculate your weight on other planets as well as the solar system facts and just general stuff about like that we wanted to make it open source first and foremost since we both were bringing it from open source projects and of course make it available to Android and iOS to have the biggest reach possible so one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:00&lt;/strong&gt; · of the reasons why this works so well is because of the great open 8 the eyes that are out there it was super easy to get started all of these api&apos;s have just REST API interfaces so you can very easily use them in any client-side application so we wanted to make it as&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:17&lt;/strong&gt; · easy to use as possible we didn&apos;t want there to be any barrier to entry when someone was download downloading the app and wanting to explore space we didn&apos;t want it to be super technical but we wanted to have the information available for the people that knew what they were looking for but also to be able to help with people that just want a very vague idea of space and maybe go to a racketeering outlet so I actually use&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:42&lt;/strong&gt; · this app all the time whenever I go to see a SpaceX launch because you can actually add the calendar events you can actually add the rocket events to your calendar so you can stay on track with the missions get moved as well as being able to see whenever the IAS is right above you and just so many other things that just the information is there what do you need it as well as sheriff little images so we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:08&lt;/strong&gt; · have a good image download or built in the app so you can actually share the images on social media or just set up as your wallpaper right on your phone and just being a week for all a couple different RSS feeds so you can have one source for a lot of different news articles for what&apos;s happening in training in space as well as multilingual lingual support we support English and Spanish and I believe Chinese right now and we wanted to make it as accessible to anybody and everybody and first and also we use a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:38&lt;/strong&gt; · ton of Google products so flutter is Google product we have it continuously integrated on the cloud platform built on a firebase backed in and a lot of other various cool technologies that we use under the hood but this is the way that we can build a scalable application that if we had a million users tomorrow we don&apos;t have to do any more maintenance and it&apos;s just about focusing on the product and not focusing on server Vegas&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:02&lt;/strong&gt; · so why flutter flutter is a open source technology built by Google that allows you to build really powerful client-side applications as well as server applications flutter runs on every platform anywhere that has a canvas flutter can paint too so they imported&lt;/p&gt;
&lt;h3&gt;Flutter&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:20&lt;/strong&gt; · clutter to raspberry pi so you can actually run your application on a Raspberry Pi with an LCD attached so it&apos;s super powerful it&apos;s super easy to get started eases dart which is a very flexible language and it&apos;s of course developer focused as well as you write&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:37&lt;/strong&gt; · the application once you deploy it everywhere you can have a desktop application or an iOS application or just a website and without having to do anything to your code you just write it once because the UI is painted on the screen you&apos;re not using like HTML directly and you&apos;re not using iOS would destroy a floater has a really awesome&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:56&lt;/strong&gt; · community so they have a really awesome select channel and there&apos;s so many contributions that happen all the time even the people on the flutter team are treated the same as people as external contributors so I&apos;ve had the opportunity to do a lot of pull requests for them as well and it just really allows you to help the product be better there&apos;s not many times that we have a way to shape the technology that we want to use so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:22&lt;/strong&gt; · and also the learning curve there&apos;s a little bit of one at the beginning but it flat it&apos;s flat after that so it&apos;s not like it&apos;s an exponential for the longer that music and this is the first technology stack that I use that I could bring truly any UI that I was using plug I you can just take a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:39&lt;/strong&gt; · screenshot or design mock-up and build the UI in like less than an hour because it&apos;s a declarative language you write it exactly how you see it and of course it&apos;s open source so you can find anything and everything down to the actual engine code all written in dart so like I said this is a collaboration project worked with cases Rodriguez and we built this entire project together&lt;/p&gt;
&lt;h3&gt;Tips for Collaboration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;6:05&lt;/strong&gt; · it took about eight months and I just wanted to share with you guys some tips for collaboration that we had to learn from real world global collaboration so first and foremost folder structure is incredibly important when you&apos;re bringing two different projects together you have to have a shared set of goals for what you want to set up the product with any person has to be able to pick up your project and know exactly where everything is without you having to walk them through what we do we leverage&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:34&lt;/strong&gt; · github and board west so that we don&apos;t overwrite each other&apos;s data we can test and make sure that it&apos;s working before we push it to the master and feedback is so important we would go to read it multiple times and we would have private pages and then just iterate upon it and that&apos;s only possible when you have continuous integration that you can you can focus on the code in the test so you can know that you&apos;re not breaking existing code that you&apos;ve already fixed we would also when we&apos;re getting closer&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:01&lt;/strong&gt; · to the release of the app we wanted to focus on an MVP because as with any project it can be really easy to just let the deadline slip and slip and slow so we really focused on just seeing okay what can we do with time we have you set a deadline and we just said okay if the features not ready we&apos;re gonna bring it on the next base and by doing that we were able to actually move a lot faster because we just focused on polishing and then focused on releasing we also flutter was&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:29&lt;/strong&gt; · really new at the time it was still in beta - and we started this project so it was changing rapidly so there&apos;d be new state management solutions that came on all the time and we also came a point where we had to freeze our code because whenever you&apos;re working on a project it may be tempting to just keep her factoring it because a new a new way comes out but we decided that any time&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:50&lt;/strong&gt; · that we set a deadline we wouldn&apos;t the upper refactor until after the release and because of that we would just choose the solution for better for worse and then we would fix it after and like I said earlier we both had strengths and weaknesses I would I became from Iowa&apos;s development of very strong and he came from Android so we were actually able to come together and work with flutter with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:12&lt;/strong&gt; · one blink code base and dart but also be able deliberation I was able to leverage like Swift and he was able to leverage Colin in the project and we split up the work and just develop a punch list so we could just knock out the features one by one so flutter is the real magic of this&lt;/p&gt;
&lt;h3&gt;The Magic of Flutter&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;8:30&lt;/strong&gt; · application of course you can build this with any tech stack you can do it natively with iOS and Android but the whole goal of doing this in flutter was that we could work with the UI and all the shared logic written in dart with one code base even though that I had a completely different background than him we both had the same background or working with flutter and it allowed us to move so rapidly and we had many other&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:54&lt;/strong&gt; · products too this wasn&apos;t like her this was definitely a side project but because of that we were able to leverage what we&apos;re learning on each of the projects and bring it to them bring to this one as well as giving back then any time that we would develop a huge thing on our project we could bring that module in and see how we could use it as well as sharing code one nice thing about dart is if you have a set up&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:16&lt;/strong&gt; · folder structure the reason why it&apos;s so important is you can drop in modules from project to project so you can take a back-end database solution and literally copy and paste it into another database solution and keep architected it correctly and that&apos;s what we were focused on doing because you know other people would use our code in other projects with flutter - and of course the powerful package ecosystem of flutter which I have developed a lot of packages for and with flutter we also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:42&lt;/strong&gt; · leverage the community with slack and Twitter this is where we interacted with tons of people and it helped us especially getting the release off because we&apos;re able to talk with the Google engineers directly on slack and say hey I&apos;m running to this is there anything that I should be looking at and they&apos;ve pointed in the right direction or even sometimes help us and do for requests on our own repo which is really awesome and it was also just really fun to develop clutter is a very fun&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:07&lt;/strong&gt; · language when you&apos;re you&apos;re developing with it because you can get up an application and just you know just 5 10 minutes and you can actually have something that&apos;s tangible and be working with it it has a thing called hop reload where you literally every time you change a line of code to change 1/2 it&apos;s instantly on the device as opposed to waiting to compile every time so that that&apos;s just been a super awesome part about the and just as a conclusion I wanted to say&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;10:35&lt;/strong&gt; · that especially when you&apos;re working on these projects make sure that your building was scaling in mind for the beginning we could have built our own server of solution easily but the problem was we needed a solution that was scaling without us having to put in a lot more time we went with the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:54&lt;/strong&gt; · intermediate solution with firebase and a Google cloud platform because we wanted to if a ton of users came tomorrow we didn&apos;t want to have to worry about spitting up a server and this is an open source project we didn&apos;t want to have to spend a lot of our time focusing on the side project we wanted to release it and just send out updates and not having to worry about the maintenance and scalability of it so that&apos;s why we really leverage the Google cloud back on with that we just lose a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:18&lt;/strong&gt; · lot of experimentation and learning there was a lot of things we did that didn&apos;t work but in the end we came up with a UI and a strategy that actually did work and you know part of especially with mobile development that I run into is UX is so important you know you may&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:34&lt;/strong&gt; · you may develop something it may technically work but that doesn&apos;t mean it&apos;s the right solution you have to find a way to build it to where there&apos;s this there&apos;s no friction when someone&apos;s using your application because the good application that doesn&apos;t get in the way that&apos;s the one that the people actually remember and also like to say just start&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:54&lt;/strong&gt; · a conversation this this only existed because we talked with each other on reddit and we wanted to bring in these things we realized that we could do more together than we could separately in our own open source projects and as with anything the more people you have working on it the better sometimes it can be especially in open source and make sure to give back so every time we would build something anew Niq or a new module we would find a way to give it back to the community where there&apos;s pulling a package or just trying to showcase what we&apos;ve been doing tutorials or giving talks because the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:26&lt;/strong&gt; · more you can help other people succeed the better that you can and like I said earlier tests are important they&apos;re hard to do and they&apos;re annoying but they help you not break the code multiple times over and over again and continuous integration just helps us constantly release faster and faster without having to focus on spending precious time developing but deploying to the app&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:49&lt;/strong&gt; · stores so if you guys want to we have the open source it&apos;s open source right now on github you can check it out like the link there are some links to the API we&apos;re using we also have a very early prototype that I submitted literally yesterday of the website flutter compiling on the web and then you can also find me on github as well as sages Twitter as well thank you so much [Music] [Applause] [Music] [Applause]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:24&lt;/strong&gt; · yes Western effort&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:41&lt;/strong&gt; · yeah I would I figured out works best for me it&apos;s just building the simplest application I could taking everything out bring it up from the beginning because for me my application that I built was a pitch pipe and I was like you can&apos;t did you just like it&apos;s a simple thing one image buttons and because of that you learn you bring in stuff as you need it not trying to overload it any other questions yes&lt;/p&gt;
</content:encoded></item><item><title>Women in Machine Learning Symposium, Flutter Forward, more dev news!</title><link>https://rodydavis.com/videos/talks/women-in-machine-learning-symposium-flutter-forward-more-dev-news/</link><guid isPermaLink="true">https://rodydavis.com/videos/talks/women-in-machine-learning-symposium-flutter-forward-more-dev-news/</guid><description>The Google Developer News Show covers latest announcements, including new dynamic CSS viewport units, and promotes major events like the Women in Machine Learning Symposium and Flutter Forward.</description><pubDate>Mon, 05 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Women in Machine Learning Symposium, Flutter Forward, more dev news!&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;E8EMabhmN_E&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;TL;DR 321 | The Google Developer News Show&lt;/p&gt;
&lt;p&gt;0:00 - Introduction
0:10 -  Web: The large, small, and dynamic viewport units → https://goo.gle/3FrLCeK
0:32 - Join us at the 2nd Women in Machine Learning Symposium → https://goo.gle/3gVBIJb
0:51 - Announcing Flutter Forward → https://goo.gle/3F4xnet
1:08 - Don’t forget to like, comment, and subscribe!&lt;/p&gt;
&lt;p&gt;Here to bring you the latest developer news from across Google is Rody from Developer Relations. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below.&lt;/p&gt;
&lt;p&gt;Follow Google Developers on Instagram → https://goo.gle/googledevs&lt;/p&gt;
&lt;p&gt;Watch more #DevShow → https://goo.gle/GDevShow&lt;br&gt;
Subscribe to Google Developers → https://goo.gle/developers&lt;/p&gt;
&lt;p&gt;#Google #Developers&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · [AUDIO LOGO] [MUSIC PLAYING] RODY DAVIS: Hi.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:04&lt;/strong&gt; · I&apos;m Rody for &amp;quot;The Developer Show,&amp;quot; and this is your weekly update on the coolest developer news from Google.&lt;/p&gt;
&lt;h3&gt;Web: The large, small, and dynamic viewport units&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:10&lt;/strong&gt; · Today we&apos;re going to talk about some exciting new CSS features available in all browsers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:15&lt;/strong&gt; · There are some new large and small viewport units which can be used to dynamically adjust based on the browser Chrome on mobile devices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:22&lt;/strong&gt; · There are also some dynamic units used to show the smallest size depending on if the toolbars are expanded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:27&lt;/strong&gt; · You can find out more at web.dev/viewport-units.&lt;/p&gt;
&lt;h3&gt;Join us at the 2nd Women in Machine Learning Symposium&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:32&lt;/strong&gt; · Next up, we have the Women in Machine Learning Symposium back for a second year in a row.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:36&lt;/strong&gt; · This year, we&apos;ll focus on the latest machine learning tools, techniques, and getting the latest scoop on the latest machine learning products from Google.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:43&lt;/strong&gt; · This event will take place on December 7 from 9:00 AM to 1:00 PM Pacific time, and you can find the link to RSVP in the description box below.&lt;/p&gt;
&lt;h3&gt;Announcing Flutter Forward&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:52&lt;/strong&gt; · Lastly, let&apos;s talk about the newly announced Flutter event in Nairobi, Kenya, called Flutter Forward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:56&lt;/strong&gt; · There will be a keynote, tech talks, ask Flutter, live session, and watch parties all around the globe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:01&lt;/strong&gt; · This event will be available online and in person, and you will not want to miss it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:05&lt;/strong&gt; · Make sure to check the link to register below.&lt;/p&gt;
&lt;h3&gt;Don’t forget to like, comment, and subscribe!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · To learn more about all these week stories, make sure to check the description box below for all the links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12&lt;/strong&gt; · Please remember to like, subscribe, share, and stay safe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:15&lt;/strong&gt; · I&apos;m Rody from &amp;quot;The Developer Show.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:17&lt;/strong&gt; · Thanks for watching, and we&apos;ll see you next week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:20&lt;/strong&gt; · [MUSIC PLAYING]&lt;/p&gt;
</content:encoded></item><item><title>Take 5 - Adaptive Dialogs - Flutter</title><link>https://rodydavis.com/videos/take-5/adaptive-dialogs-flutter/</link><guid isPermaLink="true">https://rodydavis.com/videos/take-5/adaptive-dialogs-flutter/</guid><description>This video tutorial guides developers through building a responsive &quot;adaptive dialog&quot; widget in Flutter that maintains a consistent and aesthetically pleasing appearance across desktop, mobile, and web screen sizes.</description><pubDate>Tue, 21 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Take 5 - Adaptive Dialogs - Flutter&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;AQybpww4MgQ&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Source Code: https://github.com/rodydavis/flutter_take_5&lt;/p&gt;
&lt;p&gt;In this video I walk though how to build an adaptive dialog that looks good on desktop, mobile and web. Whenever you launch a dialog you are not guaranteed that the size is constant. In this example we will show you how to fix those edge cases.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · hey guys my name is ready in this video we&apos;re gonna be building an adaptive dialogue if you want to get to where we are now make sure to watch the previous videos where we go through creating a master detailed screen as well as responsive UI and just building the flutter project at the beginning so let&apos;s go ahead and get started what we have is a simple contact master detailed&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:23&lt;/strong&gt; · scaffold and when you select a contact we see it on the right and if we were SCI&apos;s down it&apos;ll push down to the detail screen now this is great but what about when we show dialogues in the app we want to make sure this is responsive as you know if we just show an entire dialog it&apos;s gonna look really weird plus if we show a dialog like an alert dialog and then we sized down to a size that&apos;s smaller than the dialogue it&apos;s also going to look weird especially on mobile so let&apos;s go ahead and see how we can fix this first of all go ahead and create a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · new file inside of a common called adaptive dialogue and we&apos;re going to give it a make it a stateless widget so go ahead and report material do adaptive long let&apos;s go ahead and make sure we give it a size two we&apos;ll use this later on let&apos;s make it 450 by 700 we want to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:33&lt;/strong&gt; · pass in a child so let&apos;s add this to the constructor you can use the helper method right here and then it will automatically create it for you let&apos;s make this required and perfect so there&apos;s a couple different things we want to have happen if we start with a dialog from mobile we want to be able to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:54&lt;/strong&gt; · resize it to a tablet dialog when they resize up but then right back to the mobile size so what we want is a layout builder since we want this to redraw every time the screen size changes so here we have our uploader we&apos;re going to pass it a context and dimensions and the default use case is going to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:16&lt;/strong&gt; · return the adaptive dialogue but we want to check to see if we&apos;re we can even show it so first of all I&apos;m going to check to see if the dimensions dot max width is less than the K dialogue size which we&apos;re going to grab right here dot width or we wouldn&apos;t do the same thing but for the height perfect and we just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:45&lt;/strong&gt; · want to return the child since which we&apos;ll show in a second otherwise we want to return our adaptive screen or adapted dialogue so let&apos;s do a center with a child of size box from size our size will be our K dialogue size now we&apos;re&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:12&lt;/strong&gt; · also going to give it a child of an aspect ratio which will I&apos;ll explain more later but here we have our aspect ratio and we have our child and our other child awesome looks great so now&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:27&lt;/strong&gt; · let&apos;s go ahead and test it out we can go ahead and update our index which just allows us to keep all of our exports in one place and go to our contacts screen so we&apos;re going to add a button right here where you can filter the list and show different filter options so let&apos;s go to line 57 where we can add some actions to this app bar let&apos;s go ahead and create an icon button we&apos;re gonna do a filter list awesome go&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:09&lt;/strong&gt; · ahead and have it on top so first we&apos;re gonna check to see if we&apos;re in the tablet mode so let&apos;s also pass in our box constraints you&apos;re gonna do this right here actually let&apos;s put this in the front okay perfect and here we go&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:39&lt;/strong&gt; · now we&apos;re just going to take this exact same logic point in check to see if we&apos;re tablet so if we are tablet we&apos;re gonna show the dialog otherwise we&apos;re going to push to a new screen so this will be right here instead of the contact details however we&apos;re going to create our adaptive form so let&apos;s just for simplicity or exits at the bottom just create a new screen called contact list filter options cool and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:17&lt;/strong&gt; · just make this as simple scaffold with an app bar okay and we&apos;re just going to right here or turn this but make sure that it&apos;s a full screen dialog otherwise we want to show our adapter dialog so this would be show this would be show dialog context we&apos;re going to have our&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:53&lt;/strong&gt; · content or a builder rather be our alert dialog and we&apos;re gonna have our content be this sorry we need to wrap&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:19&lt;/strong&gt; · this with our adaptive dialogue so adaptive dialogue child this perfect and let&apos;s just double-check everything not be able to replace this in material but we&apos;ll check that in a second so go ahead and run it here we see that it graying out so it means that it&apos;s not the correct size let&apos;s go ahead and change this with material wonderful okay and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:55&lt;/strong&gt; · let&apos;s just make sure that it shows on mobile first so here we have our contact screen right click on filter perfect it shows our scaffold now let&apos;s click on here and we have our our dialogue which is great but it&apos;s not good because we it&apos;s quite behind everything so we can update our material object here so we can say we wanted to have a color that&apos;s transparent awesome I&apos;ll do this again&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:26&lt;/strong&gt; · and you&apos;ll see that we have our dialogue what&apos;s great is we can customize this however we want and it&apos;s just a regular widget but it has the awesome feature where if we resize down it&apos;ll go to a mobile view but if we start with mobile&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:43&lt;/strong&gt; · it&apos;s going to show the dialogue which makes sense and if we have a tablet and we resize it vertically it&apos;ll also go to these this mobile view here we have our child and it will always be the correct size and position now you can customize this however you want but this is just a silly example of how to do it in a pretty basic way later on in future&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:09&lt;/strong&gt; · videos we&apos;re going to really flush this out but just wanted to show you real quick how how easy it is to do adaptive dialogues and no matter where we are on a desktop it&apos;ll always be the correct aspect ratio okay awesome so make sure to stay tuned and look out for the next video if you have any questions please reach out to me and as always the link is in the description with the source code where you can get this up and running and thanks for watching&lt;/p&gt;
</content:encoded></item><item><title>Take 5 - Internationalization - Flutter</title><link>https://rodydavis.com/videos/take-5/internationalization-flutter/</link><guid isPermaLink="true">https://rodydavis.com/videos/take-5/internationalization-flutter/</guid><description>Learn how to internationalize a Flutter application using VS Code extensions, the `flutter_localization` package, and code generation to manage multiple languages.</description><pubDate>Tue, 28 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Take 5 - Internationalization - Flutter&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;EY7tF7ryLVw&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Source Code: https://github.com/rodydavis/flutter_take_5&lt;/p&gt;
&lt;p&gt;In this video I show you how easy it is to internationalize your Flutter application. You can use VSCode to generate the code fo you, and have Google Translate create the other languages based off of your default locale.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · hey guys my name is ready in this video we&apos;re going to internationalize our flutter applications if you want to get to where we are now please watch the other videos in the series and we build upon the application in each step so you&apos;ll get caught up right where we are first thing you need to do is open up vs code and install this extension called vs code flutter by 18 and JSON this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:21&lt;/strong&gt; · extension is really cool it allows us to generate dart code that has all the hard-coded values for us to use inside of our application so we can have autocomplete and be able to add new languages very very easily perfect so once we do that we need to open up our post by camel and add a new package so this is going to be flutter localization and this is also gonna come&lt;/p&gt;
&lt;h3&gt;add a new package&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:50&lt;/strong&gt; · from the SDK wonderful and let&apos;s go ahead and run packages get sorry this is an S photo localizations perfect okay so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:08&lt;/strong&gt; · now that we have that we need to add our generated file so open up the command palette we&apos;re gonna type in it and this will run the code generation for us we&apos;re going to just leave the default and we hit enter awesome so what it does is it creates a config for us that we can customize where the generated path is being generated here&apos;s the generated file that will we don&apos;t want to modify by hand and see it generates these functions for us as well as our JSON&lt;/p&gt;
&lt;h3&gt;open up the command palette&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:39&lt;/strong&gt; · file to write all of our strings inside so let&apos;s go ahead and add a new one called title we&apos;re going to say flutter rocks and then now once you do this you have to run this update command and this will update the generated file for you and this will do it for every language that you have awesome so once we have&lt;/p&gt;
&lt;h3&gt;update the generated file&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:03&lt;/strong&gt; · this we need to open up our main dart and start to customize this you know how we actually add the localization to the application so we&apos;re going to do a final I won a ten equal to I 1 8 and delegate I can go ahead and import this okay let&apos;s do down here at the bottom let&apos;s provide our resolution callback so this would be locale resolutions callback&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:41&lt;/strong&gt; · yeah look how resolution callback here we go and then resolution and what we&apos;re gonna do is this is basically to be if it can&apos;t determine the locale we want to fall back to a lookout that we do have supported so in this case we&apos;re just going to pour back to English wonderful and we also want to provide a supported locales and I&apos;ll link to instructions on&lt;/p&gt;
&lt;h3&gt;provide a supported locales&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:11&lt;/strong&gt; · how you can set this up an iOS to be able to update your info plist to be able to support multiple languages because this is just for the flutter application itself when you release to an app store you still need to add update the info.plist manually so we&apos;re gonna have the 118 and they&apos;re gonna have this is a part that we had that package for so global material is like material localizations step delegate&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:40&lt;/strong&gt; · right here and we need that Cupertino one so global or sorry global widgets one level widgets localization delegate wonderful you can fix our imports sorts them and let&apos;s see we also just need a locale to we can update this manually if we want so Ian us wonderful and now&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:11&lt;/strong&gt; · instead of having to hard code this title it&apos;s something that we could do now is generate this title so you can do odd generate title this gives us access to the context and this is how we&apos;re going to be adding our strings from now on we can have one eight n of context so uses inherited&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:30&lt;/strong&gt; · widget to look it up and we can looking for title that&apos;s great so now we don&apos;t have a hard-coded title anymore wonderful so let me just show you how easy it is to add new titles to our application so let&apos;s go in and let&apos;s go to our tabs because we know we have four different things so we have home about context and settings let&apos;s go in here and there&apos;s a really cool little thing that I&apos;ll show you which you can actually separate your strings by folder so I can say home and you can say title&lt;/p&gt;
&lt;h3&gt;add new titles to our application&lt;/h3&gt;
&lt;h3&gt;separate your strings by folder&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:07&lt;/strong&gt; · this would be home and you may be asking well why would I want to do that well this way you can have your strings you know well when you have code complete you can see all the individual strings sorted by the thing you&apos;re looking for so settings title will be settings we have contacts and about I believe yeah&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:43&lt;/strong&gt; · okay so once we have that we can run our update command just like before this we&apos;re going to do command palette and then update it will update our titles for us see this is what I&apos;m saying if you have the nested objects allows us to go in and scope it for what we&apos;re looking for so you can just type contacts and it filters out all the strings that are related to the contacts let&apos;s go into here and now we just do the same thing as before we do I want am of context and we&apos;re going to do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:14&lt;/strong&gt; · contacts title the cool part is you can do this very easily for multiple ones so we can do about title contacts title and this one is home title and settings title&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:34&lt;/strong&gt; · the only thing you have to import is this generated file and it takes care of everything else for you awesome so one quick thing I wanted to show you is how you can use this to generate your locales based on different languages so let me go ahead and get that set up for you if you want to support more languages than just English it&apos;s actually very easy all you have to do is obtain a Google Translate API key and this extension will do everything for you so once you have an API key you can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:04&lt;/strong&gt; · run the update command to set the API key here and once you do that you can then run the create automatic translations from the default locale in this case our default locale is English so we hit enter it will take the strings&lt;/p&gt;
&lt;h3&gt;run the create automatic translations from the default locale&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:20&lt;/strong&gt; · from your English file run over the Google Translate API and if we look we have our Spanish translation for example but what&apos;s cool is if you notice here we have the name variable on the right and Japanese it includes it on the left so it knows where to put things inside the sentence that makes sense and you could do this for as many languages you want so for example we can add some new locales as well so let&apos;s do French let&apos;s&lt;/p&gt;
&lt;h3&gt;add some new locales&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;7:54&lt;/strong&gt; · add Chinese and Russia to the same&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:11&lt;/strong&gt; · command as before see at first it&apos;s going to be empty and we&apos;re gonna run up run our command again it&apos;s gonna take a second and there it goes it generates it for us and we have French Japanese Russian and Chinese very&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:30&lt;/strong&gt; · cool now all that&apos;s left is to just go out throughout your entire application using this command as before you can also set up custom dart extensions if you want to make this easier to grab the just you know using the bill conti to have access to this object but this is so small already I feel like it&apos;s more hassle than it&apos;s worth so that does it for this video if you have any questions please leave them down in the comments below please reach out to me on Twitter and make sure to follow me on my&lt;/p&gt;
&lt;h3&gt;set up custom dart extensions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;9:03&lt;/strong&gt; · medium post to I like to blog about this stuff as well and of course I have a patreon if you want to support me thank you so much and I&apos;ll see you in the next one&lt;/p&gt;
</content:encoded></item><item><title>Take 5 - Responsive Design - Flutter</title><link>https://rodydavis.com/videos/take-5/responsive-design-flutter/</link><guid isPermaLink="true">https://rodydavis.com/videos/take-5/responsive-design-flutter/</guid><description>Learn how to implement responsive design in Flutter using an adaptive scaffold technique, allowing widgets to define layout behavior across different breakpoints for reusable code.</description><pubDate>Wed, 08 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Take 5 - Responsive Design - Flutter&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;kGhkiKp939c&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Source Code: https://github.com/rodydavis/flutter_take_5&lt;/p&gt;
&lt;p&gt;In this video I walk through how to build a flutter app with an adaptive scaffold. This is using a technique to have each widget define layout behavior based on breakpoints. This is very useful for having total reuse over all your screens. Responsive layout doesn&apos;t mean you need to have duplicate code.&lt;/p&gt;
&lt;p&gt;I also mention the package I created to abstract this for you and it can be found on pub here:
https://pub.dev/packages/navigation_rail&lt;/p&gt;
&lt;p&gt;In the upcoming videos I will go more in depth on how to adapt the individual screens. This video is mostly about how to do the main navigation in a responsive way.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · hey guys my name is ready today we&apos;re gonna be building an app with responsive design with flutter this is part of our flutter take 5 series if you want to grab the project we&apos;re working on all these videos work together so you can look at the last video and get to this point or look down in the description to grab the project alright first of all if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:19&lt;/strong&gt; · we see what we built we just built a simple app with a single screen that has a scaffold now this is great for mobile applications but what about when you want to target desktop and especially tablet so another nice tip is when&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:35&lt;/strong&gt; · you&apos;re working with flutter development I would suggest trying to build it with the desktop tooling because it allows you to have hot reload develop really quickly and to test all your different designs in a very fast manner I mean I found layout bugs and seconds after working with the desktop stuff that you just don&apos;t see when you&apos;re developing on mobile so to get started we want to define some variables for us to use so inside of our UI folder let&apos;s do create&lt;/p&gt;
&lt;h3&gt;create a file called breakpoints&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:09&lt;/strong&gt; · a file called breakpoints and inside there let&apos;s go ahead and give some variables so I&apos;m going to do tab tablet breakpoint let&apos;s do 720 and let&apos;s also do desktop&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:34&lt;/strong&gt; · let&apos;s give it 14:40 awesome so now if we end let&apos;s move this to the top level UI folder so you see we have UI and then home ok and now we want to wrap our scaffold with a layout builder so let&apos;s go ahead and do that so perfect and then let&apos;s&lt;/p&gt;
&lt;h3&gt;wrap our scaffold with a layout builder&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:10&lt;/strong&gt; · create a new file here at the top called tabs and let&apos;s go ahead and import material and we&apos;re gonna create a new screen called tab home we&apos;re gonna make&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:29&lt;/strong&gt; · this our new home for our widgets and this will be our navigation as it were so let&apos;s also create a new file our new folder called settings give it a file inside there called screen doubt Dart this will have the same as that so we can do port material let&apos;s go ahead and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:03&lt;/strong&gt; · copy this and then let&apos;s also give it one more tab let&apos;s say about screen dark and like I said everything will be in the description let&apos;s go ahead and rename it all right perfect now that we have those two screens let&apos;s go back to our tabs and here we also want to layout builder so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:35&lt;/strong&gt; · let&apos;s go ahead and return our default mobile case so this will be a scaffold with an app bar actually no app bar the bottom navigation bar get some items&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:57&lt;/strong&gt; · let&apos;s give it a current index let&apos;s go ahead and make this a stateful widget since we need a current index for this and let&apos;s out it here give the on top property&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:22&lt;/strong&gt; · and if I&apos;m going too fast you can always slow down the videos and see go to your own pace so here we&apos;re gonna add a couple different navigation bar items so let&apos;s give it a title let&apos;s call this home let&apos;s go to an icon let&apos;s give it the home icon let&apos;s give it some more tabs about settings and the appropriate&lt;/p&gt;
&lt;h3&gt;add a couple different navigation bar items&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;4:57&lt;/strong&gt; · icons for that okay perfect now we need for let&apos;s also change this type so we want to do a navigation bar type fixed and let&apos;s give it the background color of the theme scaffold background color&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:21&lt;/strong&gt; · perfect for the body we&apos;re gonna create an index stock this allows us to kind of reuse our widgets without having to rebuild them every time so we&apos;re gonna need our home screen our out screen and our settings screen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:44&lt;/strong&gt; · perfect we also need the current index it&apos;s important that these are always the same all right perfect we can use once we have this this is good so once we have this we can also kind of abstract out what we need so I&apos;m going to extract this method since we&apos;re using it multiple times and we&apos;re going to call it build body perfect and we&apos;re also&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:10&lt;/strong&gt; · going to be doing this one a lot so let&apos;s call this one on tap and we can actually make this inherited perfect okay awesome so the next thing we want to do is create our tablet so we&apos;re gonna do if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:29&lt;/strong&gt; · dimensions dot max with it&apos;s greater or equal to K tablet this is the one we just made earlier this is gonna be our tablet view now what we what do we want to have on the tablet well we probably want to have like a navigation around the side we still want our app bar at the top which will be coming from the children but then the content will be here so we want to return a material&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:54&lt;/strong&gt; · since we want to have a top level material widget let&apos;s give it a child a row let&apos;s give it some children and we&apos;re going to let&apos;s give it a navigation rail this is just got recently merged into the master so if you don&apos;t see this you can always switch to the new master Channel let&apos;s give it a current index let&apos;s define the on tap&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:26&lt;/strong&gt; · which is what we did earlier and let&apos;s get some destinations these are very similar to the bottom navigation bar items but we can still add them here so we have actually we can go ahead and rename those I think we just changed this to label yep perfect and let&apos;s go ahead and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:03&lt;/strong&gt; · create and expanded this we&apos;ll fill in the remaining and we&apos;re going to do build body perfect so now we have that but what about desktop and remember it&apos;s important to do these in the right order so here we&apos;re gonna do if dimensions dot max width is greater than equal to K&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:27&lt;/strong&gt; · desktop now we want to return the desktop let&apos;s also add another variable in here well actually we can keep it in there since it&apos;s all I can be local but we&apos;re going to do cons k side menu with let&apos;s give it to 50 and now we&apos;re going to return another material would you get a child this time we&apos;ll give it a row as well&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:02&lt;/strong&gt; · but this time we&apos;re going to turn a container with a width of this and then a child of a ListView so it&apos;s scrollable and the children will be these items except this will be list styles and this will make sense in a second so there&apos;s a couple other things we have to add but I&apos;ll come back to that so let&apos;s do this as leading&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:38&lt;/strong&gt; · we need to have a is selective property which is current index is equal to zero grind index is equal to one and current index is equal to two awesome so we can also copy this since will also have an expanded here and that looks good and we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:06&lt;/strong&gt; · also need the on top so this will be on top this is why we abstracted earlier&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:22&lt;/strong&gt; · zero and if you&apos;re curious I create a package that makes us a lot easier but in this case we&apos;re just writing it out so we have our body we have our width right irreal awesome so now we have an adaptive scaffold now let&apos;s go into each one of these and just quickly flush them out actually here we can remove this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:51&lt;/strong&gt; · layout builder for simplicity sake and let&apos;s copy this over just so you can kind of see what we&apos;re dealing with in later videos I&apos;m going to go over how you can make master detail and different types of responsiveness alright so home screen and let&apos;s change to about screen alright let&apos;s go ahead and run it go ahead and create a launch&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:19&lt;/strong&gt; · configuration and run now just going back over what I did basically we define all of our children in here for our navigation we have our breakpoints file which defines our breakpoints we have our various screens and then we just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:38&lt;/strong&gt; · call that from the main which allows us to return to our tab screen now it&apos;ll make sense why we did this this way right now because later on we can still reuse all these screens individually that the title the app bar for example is not bound to the tabs the only thing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:58&lt;/strong&gt; · this does is provide a navigation for the application so here we have let&apos;s start off with mobile and if we stretch it out we have a navigation rail which real quick we can change this to be let&apos;s change this navigation rail to look the same as tab so we can go down here get down to navigation rail I&apos;m gonna do a label type which is this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:26&lt;/strong&gt; · navigation wheel type of all and you see it&apos;s so fast to heart reload these changes and we have this on the side and if we stretch it out even further we now have our list styles you can still&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:42&lt;/strong&gt; · select them and you see it changes up here on the left if we bring in the navigation rail still works and if we bring it even further we have this down here awesome so stay tuned for the next video we&apos;re gonna take this one step further&lt;/p&gt;
</content:encoded></item><item><title>Take 5 - Your First Flutter Project</title><link>https://rodydavis.com/videos/take-5/your-first-flutter-project/</link><guid isPermaLink="true">https://rodydavis.com/videos/take-5/your-first-flutter-project/</guid><description>Learn how to set up and configure your first Flutter project, including using `flutter create` and cleaning up the initial boilerplate code in VS Code.</description><pubDate>Sat, 29 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Take 5 - Your First Flutter Project&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;rtBkU4pvHcw&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Blog Post: https://medium.com/@rody.davis.jr/creating-your-first-flutter-project-58fd0312204b
Source Code: https://github.com/rodydavis/flutter_take_5/&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · my name is Rhodey today we&apos;re gonna be starting a new series called flutter take five first in this series we&apos;re gonna be talking about how to set up your first flutter projects so if you go ahead and open up finder if you&apos;re on Mac let&apos;s go ahead and create a new folder we&apos;re going to name it my project and once you do that go ahead and drag that into vs code now right now it&apos;s just empty folder so let&apos;s go ahead and open up terminal and run flutter create&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:32&lt;/strong&gt; · period now if you&apos;re on the master channel it will add the web and Mac OS folder if you&apos;re not you&apos;re just going to see the iOS and Android so first thing you do is if you&apos;re on vs code go ahead and install the flutter extension as well as there&apos;s some other useful ones that I like pub spec assists for example and also remove blank lines&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:12&lt;/strong&gt; · right here and another one we can do is remove all comments yes or keyboard shortcuts for all these but I just think it makes it pretty easy so let&apos;s go ahead and type remove all comments remove blank lines&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:35&lt;/strong&gt; · and now you&apos;ll have your basic pups back this is where you&apos;re gonna have all your dependencies for your flutter project as well as some information about the version number name and description this is also where we&apos;re gonna include any assets but we&apos;ll go over that later so once you kind of get started with your project and you kind of remove the the&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57&lt;/strong&gt; · default boilerplate what you want to do is go ahead and go into your main duct art this is inside your live folder we&apos;re gonna go ahead and be prompted to get packages so go ahead and do that once we do that we can go ahead and start let&apos;s go ahead dismiss this do the same thing as before remove all comments and we can format by holding down option shift + F so this is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:25&lt;/strong&gt; · pretty great but still not good for a basic flutter project so let&apos;s go ahead and create a new folder called UI in that folder let&apos;s create another one called home and instead there let&apos;s create a new file called screen dart in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:43&lt;/strong&gt; · there let&apos;s go ahead and import material this is flutters package for all the components let&apos;s go ahead and type st and we&apos;ll get stateless let&apos;s go ahead and type home screen let&apos;s just give it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:00&lt;/strong&gt; · a default scaffold or a so replace container or scaffold an app bar we can make it empty for now and a body with a container go ahead and copy home screen we&apos;re gonna go in here and replace this this would be prompted to import this we can use the relative or the fully qualified import let&apos;s go ahead and delete that and once we have that that&apos;s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:31&lt;/strong&gt; · good so we started to refactor out some stuff if you&apos;re running on the simulator and you don&apos;t want to show the banner we can go ahead and hide that so show show checked debug mode banner and let&apos;s do the false let&apos;s go ahead and give this a proper title so my project let&apos;s go ahead and change this to the default light theme and if&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:55&lt;/strong&gt; · you want dark mode let&apos;s go ahead and give it a theme data dark if you want to manually switch between modes you can do theme mode thoughts system alright great so once we have that all set up you should be able to start flutter run so go ahead and open up terminal you can do&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:23&lt;/strong&gt; · flutter run - the Mac OS this makes it really easy to debug applications faster if you&apos;re using the desktop because it doesn&apos;t have to spend up the iOS simulator or Android emulators as well and cool so we go ahead and have our project if we go back over to home screen we can give it a title and hit&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:52&lt;/strong&gt; · our you know hot reload awesome thanks so much for watching I&apos;ll see you in the next episode&lt;/p&gt;
</content:encoded></item><item><title>Take 5 - Shared Preferences - Flutter</title><link>https://rodydavis.com/videos/take-5/shared-preferences-flutter/</link><guid isPermaLink="true">https://rodydavis.com/videos/take-5/shared-preferences-flutter/</guid><description>Learn how to implement Shared Preferences and Provider in Flutter to store user settings, manage dark mode, and perform fresh application installation checks.</description><pubDate>Mon, 04 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Take 5 - Shared Preferences - Flutter&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;U2jdTCMBTgE&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Source Code: https://github.com/rodydavis/flutter_take_5&lt;/p&gt;
&lt;p&gt;In this video I talk about how to get started with Shared Preferences in Flutter. We will setup dark mode, and a fresh install check for our application.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · hey guys my name is ruddy in today&apos;s video we&apos;re gonna be building an app with shared preferences and provider so this is useful if you want to store settings for the user if you want check to see if the device and application has not been run before or if you just want to store key value stores so first&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:16&lt;/strong&gt; · things first if you want to get to where we are now make sure to watch the other videos in the series and otherwise let&apos;s dive in so what you want to do is go to your post by camel and add two packages we&apos;re going to be adding shared preferences and provider so go ahead and do that we&apos;re going to do share preferences this will work on Mac OS web iOS and Android and maybe Windows by the time you&apos;re watching this and we&apos;re gonna do provider this will be so we can have access to our settings object throughout the application wonderful so&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:50&lt;/strong&gt; · once that is done getting the packages we can go to our main duct art and create this into a stateful widget great so let&apos;s get access to our shared preferences object so I can do that by using shared preferences and then press I like to use this for brevity and we can call our an it state where we update this value so I&apos;m gonna share preferences dot Cadets 10 stop then&lt;/p&gt;
&lt;h3&gt;Shared Preferences&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1:22&lt;/strong&gt; · preface is equal value cool and one last thing we can give a bowl so you say is fresh it&apos;s equal to false dismounted set state its fresh is equal to press dot get bowl is fresh installation install&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:57&lt;/strong&gt; · okay there we go this rash let&apos;s do make it a unique he is fresh app install perfect and we just want to go to default value so for the first time we want to say true since if we grab this object and it is null it means we haven&apos;t said it before therefore it is a freshener solution we&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:21&lt;/strong&gt; · can now update our debug check poke manner to say if it is fresh we want to show the banner otherwise we don&apos;t and we can go ahead and run this awesome so while that is loading I want to go ahead and work on the correct way to do this but real quick let&apos;s set our value so after we make sure that the app is fresh we want to set the value to false and that is because we never want to show&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:52&lt;/strong&gt; · this code for example you may want to check to see if your applications fresh so you can send like a notification to welcome the user to ask for certain permissions and to set some certain default values so here we have our our application and we have our debug banner here on the top but now if we refresh the debug banner goes away and this is because now it is a fresh installation anymore awesome so let&apos;s find a way to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:21&lt;/strong&gt; · improve this what you want to do is create a new top-level folder called source inside there we create a folder called classes and inside there we create a file called settings wonderful radio class settings and this is just&lt;/p&gt;
&lt;h3&gt;Setting up Shared Preferences&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3:40&lt;/strong&gt; · going to be some boilerplate I have a package that does this all for you but if you want to do that manually this is how we do it so let&apos;s create a shared preferences object so look familiar for what we just had we&apos;re gonna have a pool it is ready if this is not no&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:02&lt;/strong&gt; · we&apos;re gonna have a future method that checks to see that actually initializes everything since we&apos;re going to call this once when the application launches we&apos;re going to say did this make this async and say prefs is equal to a weight shared preferences get instance and we&apos;re gonna return is ready looking good&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:28&lt;/strong&gt; · so now we need to do a couple more things we also want to have a stream controller because we want to rebuild when new settings are updated so we can do this easily by adding a stream controller which the generic is settings just a controller is equal to let&apos;s make it a broadcast controller and can infer&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:54&lt;/strong&gt; · the type here we can remove it here and let&apos;s also give it a dispose or a closed method it&apos;s up to you so we&apos;re gonna say void closed and we&apos;re gonna do closed&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:17&lt;/strong&gt; · actually let&apos;s rename it to disposed because we&apos;re gonna be using a provider and it may make more sense for the object okay awesome and after this is done we can just quickly add a in event to our stream before and after since we&apos;re gonna be providing default values and looks great so now I can get into the settings themselves so let&apos;s consider the is fresh key for example we&lt;/p&gt;
&lt;h3&gt;Setting up Settings&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;5:47&lt;/strong&gt; · can go back into our settings and we can say let&apos;s do static cons string is fresh let&apos;s make this private is fresh key is equal to our key that we just added and now let&apos;s add our getter and setter so we can say Bogut is fresh is equal to preferences Hipple a turkey and our default value in&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:21&lt;/strong&gt; · this case we want to be true and now we have our setters so we&apos;re gonna say bull is fresh the value and we&apos;re not going to do this yet we&apos;re going to come back to this we&apos;re going to now have a a new method called future update is fresh and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:45&lt;/strong&gt; · the reason why is sometimes we want to wait for this async function to complete so now we&apos;re just going to do some quick things we&apos;re going to check to see if it&apos;s not ready and if it&apos;s not we want to initialize it so we need to make this a sink here and then we say prefs dot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:07&lt;/strong&gt; · set pool we give it our key and our value we await it here and then we just add a new object to the string this will make more sense later and then what&apos;s cool is now we can just call this method update is fresh and pass our value wonderful it looks really good and now let&apos;s just refactor our main duck dart to use this new logic we can remove all of this and change this to settings can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:45&lt;/strong&gt; · import this we can have this equal to our object we need to dispose of it before we forget and now we check we initialize it so this will be settings dot in it we can&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:09&lt;/strong&gt; · now wrap our material up with a stream builder the stream will be something we forgot to add so we can do stream of settings get stream controller dot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:25&lt;/strong&gt; · stream awesome let&apos;s go back in here we have our settings object stream and as you see part is fresh is no longer there so we can now we can just do settings dot not it&apos;s ready it is fresh wonderful&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:46&lt;/strong&gt; · wonder the thing we want to do is have access to our settings object throughout our entire location so this is pretty easy we can just wrap this with a a multi provider or a single provider but in this case from these multi provider you can do provider value we&apos;re gonna pass in our settings and also give it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:11&lt;/strong&gt; · the generic wonderful and we&apos;re going to go so let&apos;s rerun our application make sure nothing broke we have an error and we&apos;re good ok that error was just from the restart so every time we update our settings now it&apos;s gonna update this class so let&apos;s take it a step further and let&apos;s add a dark mode toggle to our application so we can just you know copy this we can say is dark mode key is dark&lt;/p&gt;
&lt;h3&gt;Dark Mode Toggle&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;9:45&lt;/strong&gt; · you can now update this and we can update this it&apos;s dark I&apos;ll just fix this is dark is&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:01&lt;/strong&gt; · dark wonderful and then the only difference this one being is this gonna have a default of false wonderful let&apos;s go into our settings and say our for a theme we&apos;re going to say if is dark we want to and our theme mode dark otherwise our theme mode dot light&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:25&lt;/strong&gt; · wonderful that should fix that error and one last thing let&apos;s go to our settings let&apos;s grab our settings objects as these settings to provider it&apos;s important provider of settings&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:53&lt;/strong&gt; · if either of settings and then let&apos;s do this and false changes to ListView awesome and let&apos;s just use a switch list i&apos;ll which is probably one of my favourite widgets that are so handy let me say is dark really give it the on&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:19&lt;/strong&gt; · change what&apos;s cool is because we&apos;ve already added that function we can just call this directly and we can give it the title text and say dark no wonderful now everything should be good let&apos;s go back to here and we see that we have a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11:38&lt;/strong&gt; · stream builder around our material it should call this every time we call the dark mode let&apos;s go to our settings click it and look at that so every time we change this it will update our application what&apos;s cool is this is a stateless widget there&apos;s only one snake foliage it in the application and that is the made art we can remove this shirt preferences import and we have our settings object which then we can add settings however you want to this and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:08&lt;/strong&gt; · it&apos;s super easy to add new settings just as I was saying but you can also support different types to we can support list of strain integer and all different kinds so let&apos;s just change this real quick to be a custom type so let&apos;s support an enum so this could be theme mode key mode and we just say let&apos;s do this the milkey here&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12:50&lt;/strong&gt; · load load updates and load copy this here and right here it thinks is a bull so let&apos;s change it to the mode wonderful we need to import flutter services they&apos;re gonna say get int since it&apos;s an enum value and otherwise we&apos;re going to return let&apos;s do theme mode dot system index cool and here we can specify our enum&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:25&lt;/strong&gt; · values we can say no values and let&apos;s do yeah wonderful so this will automatically fall back to the correct one let&apos;s see emo gets low type I think&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13:53&lt;/strong&gt; · we have a conflict there oh then we don&apos;t okay so theme mode yes we need to set it here so we&apos;d say update theme mode update the mode or it&apos;s a set integer and we&apos;re going to do Value Index awesome and then now this is a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14:09&lt;/strong&gt; · just a quick example of how you can support an enum value for share preferences and of course all the code will be in the description below you can look at the repo and go over this but just want to show you how easy it is to use shared preferences and especially provider thank you so much for watching be sure to stay tuned for next week&apos;s video and if you have any questions please reach out to me support me on patreon and follow me on github and Twitter thank you so much&lt;/p&gt;
</content:encoded></item><item><title>Take 5 - Master Detail Scaffold - Flutter</title><link>https://rodydavis.com/videos/take-5/master-detail-scaffold-flutter/</link><guid isPermaLink="true">https://rodydavis.com/videos/take-5/master-detail-scaffold-flutter/</guid><description>Learn how to build a responsive Master Detail Scaffold in Flutter, replicating the iOS UIKit pattern for dynamic navigation using a combination of `ListView` and `NavigationRail`.</description><pubDate>Mon, 13 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import SocialEmbed from &amp;quot;../../../../components/SocialEmbed.astro&amp;quot;;&lt;/p&gt;
&lt;h1&gt;Take 5 - Master Detail Scaffold - Flutter&lt;/h1&gt;
&lt;p&gt;&amp;lt;SocialEmbed platform=&amp;quot;youtube&amp;quot; id=&amp;quot;1i73KbI2Uhg&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Source Code: https://github.com/rodydavis/flutter_take_5&lt;/p&gt;
&lt;p&gt;In this video I walk though how to build a ListView that works great on desktop, mobile and tablets. Similar to UIKit Master Detail Controller we explore how to achieve a similar effect in Flutter.&lt;/p&gt;
&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:00&lt;/strong&gt; · hey guys my name is Rudy in this video we&apos;re gonna be building a master-detail scaffold for flutter if you&apos;re coming from iOS you&apos;re pretty familiar with this because this is one of the components that UIKit gives you out of the box but what about when we want to use this for flutter this is pretty easy if you want to get to where we are now we created a bottom navigation bar that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0:20&lt;/strong&gt; · goes to a navigation rail and a ListView on the left is really useful for navigation because we have a very dynamic way to reuse all of our UI and here you can see it&apos;s just a minimal amount of code and we&apos;re not having to duplicate logic depending on what screen we&apos;re on and if you want to see that we actually have that here in the common folder which you can grab in the source code so to do this we&apos;re going to be creating a contact list so let&apos;s go ahead and add a new folder called contacts let&apos;s make a screen let&apos;s go&lt;/p&gt;
&lt;h3&gt;Create Contact List&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;0:54&lt;/strong&gt; · ahead and import material we&apos;re going to call it contacts contacts screen contacts screen and let&apos;s go ahead and add this item to our navigation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:16&lt;/strong&gt; · wonderful let&apos;s go ahead and fix that import and let&apos;s go back so if we hot reload we see that we do have this new navigation item perfect so let&apos;s go ahead and start with what you would normally do when you&apos;re building out this we would let&apos;s go ahead we need to create a contact class and then have some dummy data so here we have our contact object and let&apos;s give our our&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1:45&lt;/strong&gt; · dummy data function this will allow us to have some UI to work with so what we like to do when building this is you want to at least have let&apos;s go ahead and create this into a stateful widget but we want to have a way to click on this in a responsive way so let&apos;s have a scaffold a par and we&apos;ll just call it contacts or&lt;/p&gt;
&lt;h3&gt;Create Detail Scaffold&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2:18&lt;/strong&gt; · we&apos;re not going to Center the title I&apos;ll explain more later and let&apos;s give it a body so this is just a pretty standard ListView that you&apos;ve probably done before let&apos;s give it a ListView builder and an item count so this would be your contact length and let&apos;s get access to our contact and let&apos;s just return a list&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2:52&lt;/strong&gt; · I&apos;ll go ahead and copy this in for brevity okay awesome&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:12&lt;/strong&gt; · so if you click on the contacts actually we need a hot restart for this since we want to send in our data just a standard ListView and it&apos;s nice about this too is we already had the hover effect coming from a Mac OS desktop so not nothing special nothing happens when we click on it so let&apos;s go ahead and create our detail screen I went ahead and create&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3:40&lt;/strong&gt; · this ahead of time to save time but we can walk through what I did so here we just have a simple contact screen that has an app bar details and two lists tiles that have the data for the name and the email now what we want to do is navigate to this when we select on the list item in the list aisle so here we can pass in contact and this is probably&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:08&lt;/strong&gt; · very familiar to what you&apos;ve been building already so you see click on the details looks great but it&apos;s not really great for desktop and what I mean by that is even on tablet you know when you tap on an item in the ListView this there&apos;s so much wasted space right here so let&apos;s go ahead and fix that let&apos;s go ahead and have a selection that&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4:33&lt;/strong&gt; · we want to store inside this widget this will be a value notifier and it&apos;ll take a contact by default it&apos;s gonna be set to null because you know when you first launch it there&apos;s not gonna be any item selected let&apos;s go ahead and wrap this out into our own method so we&apos;re gonna call this build list view I&apos;m going to&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:00&lt;/strong&gt; · do which it here and we want to grab access to this selection so we can do value changed and let&apos;s go ahead and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:15&lt;/strong&gt; · grab this so this will be right here we can change this lambda2 on select and we&apos;ll give it contact in here we can pass in that item and great let&apos;s see if it still works see nothing special all&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5:38&lt;/strong&gt; · we did was just extract this out into a method perfect so let&apos;s work on the responsive side so we want to wrap this with a layout builder so let&apos;s go ahead and grab this and do layout builder takes context and dimensions gonna have&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:03&lt;/strong&gt; · our default just go and return this and let&apos;s have our breakpoint so by default we just want to show this on anything greater than a tablet or greater than a mobile view so that would be greater than equal to K tablet breakpoint and here we can return it&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:26&lt;/strong&gt; · again still nothing special still showing the same exact ListView but this time what&apos;s different is here we want to update the value this selection so if you can see that when we&apos;re on the mobile view it&apos;ll push but on ListView it&apos;ll just do this all so let&apos;s go ahead and wrap this with a&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:51&lt;/strong&gt; · row and we&apos;re going to give this a container because we need to specify with let&apos;s do Const okay this view with it&apos;s going to be let&apos;s do 300 with&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:16&lt;/strong&gt; · perfect and as you can see already looking pretty good but not there yet so let&apos;s do an expanded with a value listenable builder our listenable will&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:39&lt;/strong&gt; · be this selection we can specify that as a contact for the generics I&apos;ll do context contact and child and we just&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7:56&lt;/strong&gt; · want to return the details but we&apos;ve got to make sure that it&apos;s not null so let&apos;s do if contact is equal to null which we return early or actually we&apos;re going to turn a scaffold an app bar an empty one&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:20&lt;/strong&gt; · and a body with the center as a child of text and that sex will say no contact see elected wonderful looks great let&apos;s see what it looks like so here we go we can tap on these items but this is kind of jarring for the user so let&apos;s go ahead and add a vertical divider and to width:0 already looking better&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8:54&lt;/strong&gt; · but let&apos;s also add dividers in the list view so we can do this really easily by changing this to separated giving a separated builder just takes a context index and we can return a divider of height 0 awesome&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:16&lt;/strong&gt; · already looking great so what&apos;s cool about this is you see we have this nice you know expanded view let&apos;s go ahead and see what it looks like when you don&apos;t have a contact selected so here you know you have sorry I did that too fast so here when you click on contacts&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:33&lt;/strong&gt; · you can see that we just wanted a simple app bar that you know has nothing in it no contact selected you select the contact and you can see the details on the right but then on a mobile view you can go to the details because this is kind of the normal behavior to expect and then all the way out to desktop you&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9:53&lt;/strong&gt; · see that we can even now make the detail screen it&apos;s self responsive all while keeping our layout as dry as possible this looks great on tablet looks great on desktop and works really well on mobile so let&apos;s just go through the code again all we&apos;re doing right here is we&apos;re specifying a callback for when an item is selected in the ListView we are choosing to have this ListView builder&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:18&lt;/strong&gt; · that then just passes up that selection and then we just have a detail screen and what&apos;s cool about this is this does not know what size it is it doesn&apos;t care about that size it always shows its app bar and in here we only show this view if we&apos;re greater than the tablet you&apos;ll notice that we did not use the max the media query size because if we want this widget to itself be responsive let&apos;s say if we have if we were doing this detail screen because we want to have this as reusable as possible so that&apos;s it for&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10:49&lt;/strong&gt; · this video in the next couple videos we&apos;re going to be exploring more concepts on how to make responsive UI even better but thanks for watching and hope to see in the next one&lt;/p&gt;
</content:encoded></item><item><title>Color Utilities in JavaScript</title><link>https://rodydavis.com/web/snippets/color-utilities/</link><guid isPermaLink="true">https://rodydavis.com/web/snippets/color-utilities/</guid><description>JavaScript utilities are provided for converting color values seamlessly between RGB, HSL, and HEX color models.</description><pubDate>Sun, 19 Jan 2025 03:37:33 GMT</pubDate><content:encoded>&lt;h1&gt;Color Utilities in JavaScript&lt;/h1&gt;
&lt;p&gt;Color utilities generated by &lt;a href=&quot;https://github.com/features/copilot&quot;&gt;GitHub Copilot&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Convert an RGB color to HSL&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function rgbToHsl(r, g, b) {
    r /= 255;
    g /= 255;
    b /= 255;
    var max = Math.max(r, g, b);
    var min = Math.min(r, g, b);
    var h, s, l = (max + min) / 2;
    if (max == min) {
        h = s = 0;
    } else {
        var d = max - min;
        s = l &amp;gt; 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
            case r:
                h = (g - b) / d + (g &amp;lt; b ? 6 : 0);
                break;
            case g:
                h = (b - r) / d + 2;
                break;
            case b:
                h = (r - g) / d + 4;
                break;
        }
        h /= 6;
    }
    return [h, s, l];
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Convert HEX to RGB&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Convert HSL to HEX&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function hslToHex(h, s, l) {
    var r, g, b;
    if (s == 0) {
        r = g = b = l;
    } else {
        var hue2rgb = function hue2rgb(p, q, t) {
            if (t &amp;lt; 0) t += 1;
            if (t &amp;gt; 1) t -= 1;
            if (t &amp;lt; 1 / 6) return p + (q - p) * 6 * t;
            if (t &amp;lt; 1 / 2) return q;
            if (t &amp;lt; 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
            return p;
        };
        var q = l &amp;lt; 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1 / 3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1 / 3);
    }
    return &amp;quot;#&amp;quot; + (1 &amp;lt;&amp;lt; 24 | r &amp;lt;&amp;lt; 16 | g &amp;lt;&amp;lt; 8 | b).toString(16).slice(1);
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Material 3 to Material 2 Theme Adapter</title><link>https://rodydavis.com/web/snippets/m3-to-m2-adapter/</link><guid isPermaLink="true">https://rodydavis.com/web/snippets/m3-to-m2-adapter/</guid><description>This guide provides a CSS adapter to style Material 2 components using the modern color variables defined by Material 3.</description><pubDate>Sun, 19 Jan 2025 05:46:33 GMT</pubDate><content:encoded>&lt;h1&gt;Material 3 to Material 2 Theme Adapter&lt;/h1&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;How to style Material 2 components with Material 3 in CSS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;:root {
  --mdc-theme-primary: var(--md-sys-color-primary);
  --mdc-theme-on-primary: var(--md-sys--coloron-primary);
  --mdc-theme-background: var(--md-sys--colorbackground);
  --mdc-theme-on-background: var(--md-sys--coloron-background);
  --mdc-theme-on-surface-variant: var(--md-sys--coloron-surface-variant);
  --mdc-theme-surface-variant: var(--md-sys--colorsurface-variant);
  --mdc-theme-on-surface: var(--md-sys--coloron-surface);
  --mdc-theme-surface: var(--md-sys--colorsurface);
  --mdc-theme-text-primary-on-background: var(--md-sys--coloron-surface-variant);
  --mdc-theme-outline: var(--md-sys-color-outline);
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>