<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Derek Bingham ☘️</title>
    <description>The latest articles on DEV Community by Derek Bingham ☘️ (@deekob).</description>
    <link>https://dev.to/deekob</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F372078%2Ffaefa940-f3bd-4582-a8e9-8660dc2a650f.jpg</url>
      <title>DEV Community: Derek Bingham ☘️</title>
      <link>https://dev.to/deekob</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deekob"/>
    <language>en</language>
    <item>
      <title>How I Built My First App with Kiro</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Fri, 12 Dec 2025 06:08:52 +0000</pubDate>
      <link>https://dev.to/aws/how-i-built-my-first-app-with-kiro-1569</link>
      <guid>https://dev.to/aws/how-i-built-my-first-app-with-kiro-1569</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A few months back my son and I &lt;a href="https://dev.to/aws/game-building-fun-with-a-12-year-old-and-the-amazon-q-developer-cli-2khg"&gt;built a game using QCLI&lt;/a&gt;. This took me on a journey to understand agentic coding and prompt engineering, I then shared this journey with others in a talk &lt;a href="https://www.youtube.com/watch?v=KIIZoXLAZOU&amp;amp;t=1s" rel="noopener noreferrer"&gt;The continued developer evolution&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;I've been creating applications using agentic coding tools ever since.  However, recently I decided that instead of mindlessly prompting my way through another prototype that would work once and confuse me forever, I tried something called spec-driven development with &lt;a href="https://kiro.dev/downloads/?trk=c09e10a1-6feb-4902-897f-846979733114&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; another new tool in the crowded agentic IDE market.&lt;/p&gt;

&lt;p&gt;Spoiler: I didnt build a game this time ( as my son was at school :) ) I actually built a task manager app, to help me keep on top of all the things I havent done so far this year. The application needed: priorities, due dates and filtering. To build this application I turned to an immerging process called Specification Driven Development ( SDD ) and the tool to support this process is called KIRO&lt;/p&gt;

&lt;p&gt;Here’s how it went down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with My Old Workflow
&lt;/h2&gt;

&lt;p&gt;I’ll be honest with you - I’ve been riding the AI coding wave hard. Type what I want, watch code appear, test it, call it a day. However when I revisited these projects, a week or even a month later, I'd think “wait, why did I build it this way?”&lt;/p&gt;

&lt;p&gt;Well I could go through the chat history and find all the nuggets of my lost thought process, or look at the git history log - but this wasnt a reliable process.  Also my architecture was a mystery and my requirements - lost in time.  &lt;/p&gt;

&lt;p&gt;All I could think of was if I had a team working with me, how could I explain the code to them ? The answer was I couldnt...  and to me this wasnt acceptable when building real working software. This was “vibe coding” at its 'finest' - and it was starting to annoy me - there's got to be a better way ?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Kiro
&lt;/h2&gt;

&lt;p&gt;I started trialing Kiro during one of these “there has to be a better way” moments  . Instead of jumping straight into code generation, it made me think through three levels: requirements, design, and implementation. This process formalised a new approach called Specification Driven Developent or SDD. Each level creating actual markdown files that get version-controlled with your code and so giving some structure to the previous chaos of vibe coding.  So what is Kiro and how do we get started ?&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with KIRO
&lt;/h2&gt;

&lt;p&gt;This was super easy, I downloaded KIRO from &lt;a href="https://kiro.dev/downloads/?trk=c09e10a1-6feb-4902-897f-846979733114&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;here&lt;/a&gt; and signed in with my &lt;a href="https://community.aws/builderid?trk=56709155-8730-4104-b9d5-abcd570d492a&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;BuilderID&lt;/a&gt; and I was presented with a window much like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksd8nlvj3jh58vuzf3jw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksd8nlvj3jh58vuzf3jw.png" alt="The parts of Kiro" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then read a few articles on KIRO &lt;a href="https://builder.aws.com/content/2zjzN1BYueigEUP7gx7tRCWytJ7/top-8-tips-when-working-with-kiro" rel="noopener noreferrer"&gt;best practices&lt;/a&gt; and &lt;a href="https://github.com/094459/sdd-workshop" rel="noopener noreferrer"&gt;finished this workshop&lt;/a&gt;, just to get familiar with everything around KIRO and the SDD process that it supports.&lt;/p&gt;

&lt;p&gt;Lets start building...&lt;/p&gt;

&lt;h2&gt;
  
  
  Building My Task Manager
&lt;/h2&gt;

&lt;p&gt;I decided to test my new found understanding of SSD and Kiro by building my new task manager application with priorities, due dates, and filtering. Nothing groundbreaking, but complex enough to see if this spec-driven thing actually worked.  With SDD its important that I have an idea of what I want before starting so here is my initial sketches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcl38dc2f7fihcljvpu1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcl38dc2f7fihcljvpu1.png" alt="My First Design drawings" width="800" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see I'm not much of an artist :)&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 0: Steering and Architecture
&lt;/h3&gt;

&lt;p&gt;Before we can start building we need to define the guardrails and rules that govern our application.  These will help guide the AI as it generates the code. To do this I first created some steering files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furbep38cdo7z1rbt5y75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furbep38cdo7z1rbt5y75.png" alt="Steering files types" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I choose workspace only as the location for these steering files and filled in the details I wanted from the resulting blank file that was created. Here is some of my steering rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;When creating Python code, use the following guidance:

- Use Flask as the web framework
- Follow Flask's application factory pattern
- Use Pydantic for data validation
- Use environment variables for configuration
- Implement Flask-SQLAlchemy for database operations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also could have added in some MCP servers in Phase0 to add additional context for Kiro to understand what I'm trying to build. MCP servers like the AWS Docs Knowledgebase MCP Server to provide the latest AWS docs, and the AWS Pricing MCP Server to provide me with Pricing information.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Requirements - Lets gooo !
&lt;/h3&gt;

&lt;p&gt;Next its time to tell Kiro what to build, from the "Lets Build" panel, I made sure that "Spec" is selected and typed: “Build a task manager with priorities and due dates.”&lt;/p&gt;

&lt;p&gt;Kiro then generated detailed user stories in something called EARS notation in a requirements.md file.  Now to some EARS might be a new thing, but to me it was something from my past( it actually came out in 2009 !! ) , as a technique I had used extensively on some larger projects I worked on. Think of it as a standard way of stating requirements. &lt;/p&gt;

&lt;p&gt;So an EARS statement would typically be like: &lt;/p&gt;

&lt;p&gt;'While &lt;strong&gt;optional pre-condition&lt;/strong&gt;, when &lt;strong&gt;optional trigger&lt;/strong&gt;, the &lt;strong&gt;system name&lt;/strong&gt; shall &lt;strong&gt;system response&amp;gt;&lt;/strong&gt;'&lt;/p&gt;

&lt;p&gt;So Kiro generated extensive requirements like:&lt;/p&gt;

&lt;p&gt;[WHEN] the user creates a task without a due date [THEN] the system [SHALL] set the default due date to 7 days from creation&lt;br&gt;
[WHEN] the user attempts to save a task with an empty title [THEN] the system [SHALL] display an error message&lt;/p&gt;

&lt;p&gt;These weren’t things I asked for. They were edge cases I would’ve discovered through production bugs at 2am. The AI was thinking ahead, documenting assumptions that would’ve otherwise lived only in my head. Pretty cool&lt;/p&gt;

&lt;p&gt;I reviewed the requirements, and wanted to add some specifics about filtering and sorting. So to do this I could either use the chat interface again OR ( and this is what I did ) add the new requirements manually.  Once that was done I saved the file and make sure that Kiro was aware of these changes I put a prompt into the chat interface:&lt;br&gt;
'I have updated the requirements - please reload and review', and moved on to the next Phase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7oombtusbinl56jmvb6f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7oombtusbinl56jmvb6f.png" alt="Requirements" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Design - The “Aha!” Moment
&lt;/h3&gt;

&lt;p&gt;This is where Kiro really changed my perspective. It took my requirements and proposed a complete technical design: data models, API endpoints, component structure, database schemas. Everything laid out in a document I could review, modify, and reference later.&lt;/p&gt;

&lt;p&gt;For my task manager, it suggested: the following&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A React frontend with clear component hierarchy&lt;br&gt;
REST API with specific endpoints&lt;br&gt;
SQLite for local storage&lt;br&gt;
State management patterns&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The design phase forced me to think about architecture before writing a single line of code. In my old workflow, I’d be three prompts deep into implementation before realizing my data model was completely wrong. &lt;/p&gt;

&lt;p&gt;Here, I was able to catch issues early. As I had clearly articulated inside the steering files what the rules of this project were. So Kiro knew already what tech stack I wanted (if you have used other agentic coding assitants like claude code, you might call these rules )  So now every piece of generated code aligned with how I actually work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Implementation - Breaking It Down
&lt;/h3&gt;

&lt;p&gt;Once I had reviewed, iterated and finally was happy with the design, Kiro broke everything into discrete implementation tasks. It also gave me two options - to create some tasks as optional ( for a quicker MVP ), or to make all tasks mandatory. I chose the former&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gf6fv200pew871kzhs2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gf6fv200pew871kzhs2.png" alt="Tasks" width="800" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each task was trackable. I could execute them one at a time or let Kiro’s Autopilot mode handle multiple tasks at once.\&lt;/p&gt;

&lt;p&gt;The tasks weren’t vague like “create the frontend” - they were specific - here are some examples: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Set up project structure and dependencies&lt;br&gt;
Implement Task data model with validation&lt;br&gt;
Create TaskList component with filtering&lt;br&gt;
Build API endpoints for CRUD operations&lt;br&gt;
Add error handling and edge cases&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I could review each task’s implementation, see exactly what code was generated, and understand how it connected to my original requirements. This traceability was exactly what I was after as show below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxaognwcalsm6mq858ram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxaognwcalsm6mq858ram.png" alt="Trace your tasks" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What if things go wrong ?
&lt;/h2&gt;

&lt;p&gt;So in the implementation phase there was a number of times that task would not complete.  However when this happened I could re-run these tasks and Kiro would open a new chat window, so clearing the context, before reloading the steering and design files.  This allows it to easily pick up were it left off - a very handy feature when building complex applications with AI tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Test: Adding Features Later
&lt;/h2&gt;

&lt;p&gt;A week later, I wanted to add tags to tasks. In my old workflow, this would mean:&lt;br&gt;
Searching through code history, logs and prompt history trying to figure out where things live.&lt;/p&gt;

&lt;p&gt;Prompting the AI with incomplete context&lt;br&gt;
Hoping I didn’t break anything(  never successfully ) &lt;/p&gt;

&lt;p&gt;However now with Kiro, I just updated my requirements document with two lines about tagging functionality. Kiro automatically updated the design, created new implementation tasks, and modified the existing code to support tags. Everything was version-controlled, documented, and traceable.&lt;br&gt;
It felt like having old skool pair programming and having another engineer to vounce ideas off, who actually remembers what you decided last week.&lt;/p&gt;

&lt;h2&gt;
  
  
  A word on testing
&lt;/h2&gt;

&lt;p&gt;I think its important to also point out that some of the tasks Kiro generated to satisfy my requirements and design tasks, was to create unit test suits to verify everything.  However, unit tests are not the only way developers verify their code is fit for purpose. We also had techniques like Business Driven Developement(BDD) and Test Driven Development (TDD) ( test first ) . The good new is, a similar techniques Property Based Testing(PBT) is included in Kiro's workflow out of the box!! &lt;/p&gt;

&lt;p&gt;So at the end of an hour or so of furious Kiro-ing I ended up with this thanks to SDD and it worked ! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt0ol7jqnh9zcjfz67eq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt0ol7jqnh9zcjfz67eq.png" alt="The Finished App" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Given more time
&lt;/h2&gt;

&lt;p&gt;If I had more time ( hopefully I will ) my next stage would be to use this process to create a build and deployment pipeline so I can deploy the Task Manager website into the cloud.  &lt;/p&gt;

&lt;p&gt;Other things I would love to investigate is another feature of Kiro called &lt;a href="https://dev.to/aws-builders/aws-power-up-kiro-with-kiro-powers-5620"&gt;Kiro Powers&lt;/a&gt;. These  specialized modules / plugins for Kiro, add expertise in specific tech stacks like AWS, Stripe, or Figma, by providing tools and workflows that support building with that stack. So by bundling tools, best practices, and code into easy-to-install packages, Kiro can then produce better outcomes and accelerate development tasks.  I've linked a nice article about Powers above and its definatley on my todo list to implement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Learned
&lt;/h2&gt;

&lt;p&gt;So what did I actually learn by building this..&lt;br&gt;
&lt;strong&gt;Your specs become super prompts.&lt;/strong&gt; Your requirements and design documents stick around. Unlike chat history that disappears into the void, these are durable context the AI remembers across your entire project.&lt;br&gt;
&lt;strong&gt;Slowing down speeds you up.&lt;/strong&gt; Taking time to review requirements and design actually makes development faster. You catch architectural issues before they become technical debt that haunts you at 3am.&lt;br&gt;
&lt;strong&gt;AI needs structure.&lt;/strong&gt; Kiro’s approach keeps the AI focused and aligned with what you actually want. It’s true collaboration, not just “generate code and pray.”&lt;br&gt;
&lt;strong&gt;Documentation is free.&lt;/strong&gt; Your specs double as project documentation. When your teammate asks “why does it work this way?” you have an actual answer, not “uh, the AI did that I guess?”&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should You Actually Use This?
&lt;/h2&gt;

&lt;p&gt;Spec-driven development isn’t for everything. Building a quick proof-of-concept / or architectural spike to test an API or experimenting with something you’ll throw away in an hour? ? Just vibe code it with prompts and get a quick feedback cycle happening&lt;/p&gt;

&lt;p&gt;But for anything you’ll maintain, expand, or collaborate on, SDD provides the structure that prevents your codebase from becoming a mystery novel where you’re both the author and confused reader.&lt;/p&gt;

&lt;p&gt;Kiro gives you the best of both worlds: the speed of AI-assisted development with the clarity of actually knowing what my code does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;With Kiro and SDD, if you can write a prompt, you can create a spec. The &lt;a href="https://github.com/094459/sdd-workshop" rel="noopener noreferrer"&gt;SDD Workshop&lt;/a&gt; by my esteemed colleage &lt;a class="mentioned-user" href="https://dev.to/094459"&gt;@094459&lt;/a&gt; has hands-on tutorials that walk you through building complete features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shift from vibe coding to spec-driven development felt like going from scribbling notes on napkins to using a proper notebook. Both work, but one of them lets you find your notes later.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Now its your go
&lt;/h2&gt;

&lt;p&gt;Get started just like me by doing this &lt;a href="https://github.com/094459/sdd-workshop" rel="noopener noreferrer"&gt;SDD Workshop on GitHub&lt;/a&gt; ( thx &lt;a class="mentioned-user" href="https://dev.to/094459"&gt;@094459&lt;/a&gt; ) for hands-on tutorials, or download Kiro from &lt;a href="https://kiro.dev/downloads/?trk=c09e10a1-6feb-4902-897f-846979733114&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;kiro.dev&lt;/a&gt; and see how spec-driven development changes your workflow.*&lt;/p&gt;

</description>
      <category>aws</category>
      <category>genai</category>
      <category>kiro</category>
    </item>
    <item>
      <title>How a 12 year old built a game with Amazon Q Developer CLI.</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Tue, 15 Apr 2025 03:38:52 +0000</pubDate>
      <link>https://dev.to/aws/game-building-fun-with-a-12-year-old-and-the-amazon-q-developer-cli-2khg</link>
      <guid>https://dev.to/aws/game-building-fun-with-a-12-year-old-and-the-amazon-q-developer-cli-2khg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Last weekend, my 12-year-old son  and I embarked on a game development adventure that transformed our usual Sunday afternoon gaming session into something a bit different. Armed with Pygame, the new Amazon Q Developer CLI, and an experimental approach we called  "child coding" we decided rather than play games, lets build one.  So thats what we did ! -  but in the end we managed to create not one but two simple games in a single day. &lt;/p&gt;

&lt;h2&gt;
  
  
  Here's how it went down.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Setting Up Our Environment
&lt;/h2&gt;

&lt;p&gt;This was easy, all we had to do was install the Amazon Q Developer CLI. We would then use the capabilities of this tool to work out what else to install and how to install it, including any of the other software we needed on our quest.&lt;br&gt;&lt;br&gt;
The easiest way to get started, we found was to use &lt;a href="https://dev.to/aws/the-essential-guide-to-installing-amazon-q-developer-cli-on-linux-headless-and-desktop-3bo7?trk=c09e10a1-6feb-4902-897f-846979733114&amp;amp;sc_channel=blog"&gt;THIS LINK&lt;/a&gt;, this excellent blog by  Ricardo is a one stop shop on how to set everything up.  &lt;a href="https://github.com/aws/amazon-q-developer-cli?trk=c09e10a1-6feb-4902-897f-84697973311&amp;amp;sc_channel=blog" rel="noopener noreferrer"&gt;Q Developer CLI&lt;/a&gt; is a coding assistant that brings generative AI capabilities directly to the command line. While not specifically designed for game development, we discovered it's remarkably effective when paired with Pygame.&lt;/p&gt;

&lt;p&gt;We knew we had everything running by executing from a standard shell env.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;q doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9z7z7ctal4qna774k9aj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9z7z7ctal4qna774k9aj.png" alt="Che k if Q CLI is Installed" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a game engine
&lt;/h2&gt;

&lt;p&gt;For this we decided on &lt;a href="https://github.com/pygame/pygame" rel="noopener noreferrer"&gt;Pygame&lt;/a&gt; which is a a free and open-source cross-platform library for the development of multimedia applications like video games using Python. I had previously been shown what Pygame could do and thought it would be a great fit.  Its python so pretty easy to pick up and my son had very limited coding experience to begin with and installation was easy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pygame
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Our First Game: "PacNibbles"
&lt;/h2&gt;

&lt;p&gt;For our first creation we reused an idea I had heard about previously, that is, creating a game that is a mashup of two other games.  So we created PacNibbles - a mashup of Pacman and Snake(nibbles). Instead of meticulously planning every detail, we embraced "child coding" - a term loosely based on "vibe coding", coined for our approach of starting with a core game play feeling and iteratively building toward it, using AI to fill in the technical gaps. Once we had our idea we then started to prompt the AI Assistant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;q /chat &lt;span class="s2"&gt;"write a pygame game which combines the ideas of pacman and nibbles"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feanxqwyv18lva2jpg51y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feanxqwyv18lva2jpg51y.png" alt="First Q Dev CLI output" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is were Q Developer CLI started doing all the heavy lifting for us.  It created a code repo, created a ReadMe.md and started to fill out the python file pacnibbles.py with code. Once complete all assertions and mechanics were outputted to the console&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0fjs6xclul4cg3lrcvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0fjs6xclul4cg3lrcvm.png" alt="Look it tells us what to expect" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So this was the end of our first iteration and guess what ?  When we started the game it worked !! BUT it was very 'blocky' and not fun to play. So we started some "child coding" iterations.  Our next prompt was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; I now want power pills that &lt;span class="nb"&gt;let &lt;/span&gt;me east the ghost added to the game
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then we added better graphics&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;Great - I now want the game to look really &lt;span class="nb"&gt;nice &lt;/span&gt;with enhanced graphics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our "vibe coding" technique followed these principles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Focus on the game feel first&lt;/li&gt;
&lt;li&gt;Build the minimum viable prototype&lt;/li&gt;
&lt;li&gt;Use AI to overcome technical roadblocks&lt;/li&gt;
&lt;li&gt;Test frequently with our target audience (Kiddo's friends)&lt;/li&gt;
&lt;li&gt;Iterate based on immediate feedback&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach proved perfect for our father-son project. Kiddo could focus on the creative aspects while I handled the technical, with Amazon Q Developer bridging our knowledge gaps. &lt;br&gt;
In the end we had this game looking quite nice and playing well&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykxzxt550l27bx8p5m7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykxzxt550l27bx8p5m7l.png" alt="The final pacnibbles game" width="451" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our Second Game: "Clowns Adventure"&lt;/p&gt;

&lt;p&gt;Encouraged by our success with PacNibbles, we moved on to a more ambitious project: a simple 2D platformer called "PlumberPlatformer." This time, we needed more complex mechanics like jumping, platforms, and gravity. Rather than writing everything from scratch, we leveraged Amazon Q's ability to generate boilerplate code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q "create a pygame thats a platformer with a character with gravity, jumping and platform collision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI output gave us a solid starting point that we could then customize to fit our game's vibe and this is how the first iteration turned out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzojaft5vdlg0xb20irdn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzojaft5vdlg0xb20irdn.png" alt="Clowns adventure screenshot" width="451" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will work on that next weekend :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Our weekend project taught us several valuable lessons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AI coding assistants can dramatically accelerate development, especially for educational projects&lt;/li&gt;
&lt;li&gt;"Vibe / child coding" - focusing on the feel and iterating rapidly - works well for game development&lt;/li&gt;
&lt;li&gt;Parent-child coding is more engaging when both contribute to the creative direction&lt;/li&gt;
&lt;li&gt;Pygame remains an excellent entry point for young game developers&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;One day, two games, and a whole lot of fun,  we  had created something we were mostly proud of. Our games weren't perfect, but they were fun, and more importantly, they were ours. For parents looking to connect with their children through coding, I highly recommend this combination of Pygame and Amazon Q Developer AI CLI coding assistance. The barrier to entry has never been lower, and the creative possibilities have never been greater.&lt;/p&gt;

&lt;p&gt;If you want to get started too ??&lt;/p&gt;

&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-installing.html??trk=c09e10a1-6feb-4902-897f-846979733114&amp;amp;sc_channel=blog" rel="noopener noreferrer"&gt;Download and install Amazon Q Developer CLI &lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;p&gt;Thanks for reading, if you manage to build some games better than ours - please comment below :)&lt;/p&gt;

</description>
      <category>aws</category>
      <category>development</category>
      <category>coding</category>
    </item>
    <item>
      <title>Using the Flutter BLoC Pattern with AWS Amplify Datastore</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Mon, 30 Aug 2021 07:11:44 +0000</pubDate>
      <link>https://dev.to/aws/using-the-flutter-bloc-pattern-with-aws-amplify-5511</link>
      <guid>https://dev.to/aws/using-the-flutter-bloc-pattern-with-aws-amplify-5511</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Last year I began really looking into Flutter development as AWS released the beta version of the &lt;a href="https://github.com/aws-amplify/amplify-flutter" rel="noopener noreferrer"&gt;AWS Amplify Flutter client libraries&lt;/a&gt;. These &lt;a href="https://aws.amazon.com/blogs/aws/amplify-flutter-is-now-generally-available-build-beautiful-cross-platform-apps/" rel="noopener noreferrer"&gt;Amplify Flutter libraries are now GA&lt;/a&gt; &lt;br&gt;
and I have started building out applications that show the offline sync functionality of AWS Amplify's DataStore category.&lt;/p&gt;

&lt;p&gt;So as I built out various UI's that used DataStore, I quickly ran into the old problem of how to handle state and emit/consume events as states changed.  This is where I came across the &lt;a href="https://bloclibrary.dev/#/" rel="noopener noreferrer"&gt;BLoC&lt;/a&gt; pattern and its simplified sibling the &lt;a href="https://blog.flutterando.com.br/cubit-a-simple-solution-for-app-state-management-in-flutter-66ab5279ef73" rel="noopener noreferrer"&gt;Cubit&lt;/a&gt;. So I thought I would write a quick post about it as I loved its simplicity and elegance in wrapping the AWS Amplify DataStore calls.  &lt;/p&gt;

&lt;p&gt;In this post I will walk you through an example of the BLoC pattern, how you can use it to improve your use of AWS Amplify Datastore API calls, to be a lot cleaner and so easier to maintain.&lt;/p&gt;
&lt;h1&gt;
  
  
  Abstract Data Access
&lt;/h1&gt;

&lt;p&gt;For those who do not know about &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/amplify-datastore-implementation/amplify-datastore-overview.html" rel="noopener noreferrer"&gt;Amplify DataStore&lt;/a&gt;, it provides a programming model for leveraging shared and distributed data without writing additional code for offline and online scenarios, which makes working with distributed, cross-user data just as simple as working with local-only data. If we boil this down, it simply means that working with a single set of DataStore API calls means that data will not only be persisted locally, but also synced with a connected cloud back end.&lt;/p&gt;

&lt;p&gt;The Datastore API calls, like any traditional data persistence mechanism are best wrapped in a &lt;a href="https://deviq.com/design-patterns/repository-pattern" rel="noopener noreferrer"&gt;repository pattern&lt;/a&gt; - I have been wrapping these calls in a repository so that I can abstract them away from the application logic and make them testable.&lt;/p&gt;
&lt;h1&gt;
  
  
  Understanding BloC
&lt;/h1&gt;

&lt;p&gt;First lets have a quick overview of state and in particular the use of the BLoC pattern in Flutter.  The pattern itself is very much in the reactive style, and stands for Business Logic Components. The gist of BLoC is that everything in the app should be represented as stream of events: widgets submit events; other widgets will respond. BLoC sits in the middle, managing the conversation. An added benefit is that Dart comes with syntax for working with streams that is baked into the language. Lets see how a typical program would flow using  the Bloc and Repository patterns with the UI Layer&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkrotfsyacotuof5uf2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkrotfsyacotuof5uf2h.png" alt="BloC Flow" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can seem, data flows from the UI layer, via the BLoC layer into the repository. The example shows the repository wrapping a &lt;code&gt;Amplify.DataStore.save()&lt;/code&gt; call. &lt;/p&gt;
&lt;h1&gt;
  
  
  Understanding the Cubit
&lt;/h1&gt;

&lt;p&gt;As explained the BLoC uses a stream of events and in its natural form isn't that easy to then wire into UI widgets.  To listen and take action on events generated by widgets in Flutter we can use the Cubit, a simplified way of implementing BLoC in your application.  The main difference between BloC and Cubit is that Cubit is suitable for simple state management where you just have one kind of event to bind to the state. While Bloc is for complex state management where you can have many events to map to states.  This is what I have been using when interacting with the my repository that wraps Amplify DataStore calls. I often find keeping things simple is the way forward.&lt;/p&gt;
&lt;h1&gt;
  
  
  Putting together a simple example
&lt;/h1&gt;

&lt;p&gt;So to demonstrate this I will walk you through the steps required to implement a simple 'update' action using Cubit.  The first thing we need to do is install the dependencies we need in pubspec.yml&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo83vngpla1rli4ru3lu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo83vngpla1rli4ru3lu8.png" alt="Dependancies" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can see the dependencies I need to run AWS Amplify along with the Flutter BLoC libraries. Once they are pulled down, next we create our repository class to wrap our DataStore API calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:amplify_flutter/amplify.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_droidcon/models/ShoppingListItem.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShoppingListItemRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ShoppingListItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getListItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ShoppingListItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DataStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ShoppingListItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;classType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not query DataStore: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createListItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;itemName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ShoppingListItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;itemName:&lt;/span&gt; &lt;span class="n"&gt;itemName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;isComplete:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DataStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updateListItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ShoppingListItem&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isComplete&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;updatedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;isComplete:&lt;/span&gt; &lt;span class="n"&gt;isComplete&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DataStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updatedItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Stream&lt;/span&gt; &lt;span class="n"&gt;observeTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Amplify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DataStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ShoppingListItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;classType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code has a number of data operations &lt;code&gt;get/create/update&lt;/code&gt; that take the passed in args that are then used to construct the Amplify DataStore API calls.&lt;/p&gt;

&lt;p&gt;The next stage is to create the Cubit that wraps these calls. The Cubit for the above repository class looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShoppingListItemCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Cubit&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ShoppingListState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_shoppingListRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ShoppingListItemRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="n"&gt;ShoppingListItemCubit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoadingShoppingList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;getListItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ShoppingListSuccess&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoadingShoppingList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_shoppingListRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getListItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ShoppingListSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;listItems:&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;createListItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;itemName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_shoppingListRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createListItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;updateListItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ShoppingListItem&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isComplete&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_shoppingListRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateListItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isComplete&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;observeItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;itemStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_shoppingListRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;observeTodos&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;itemStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getListItems&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that inside some of the methods in this class we can also &lt;code&gt;emit()&lt;/code&gt; state changes.  Finally, lets see how to use the Cubit defined above. Using the &lt;code&gt;updateListItem&lt;/code&gt; action as an example, what does that look like inside a UI widget.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;_shoppingItemListView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ShoppingListItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;itemCount:&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;itemBuilder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;CheckboxListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;itemName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;value:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isComplete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;onChanged:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;BlocProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ShoppingListItemCubit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateListItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see implementation of the pattern inside the widgets &lt;code&gt;onChanged&lt;/code&gt; property is trivial and we simply look up the Type of object we want the BlocProvider for, the send the  value down to the BloC class's method - which in turn delegates to the repository.  Very simple but extremely effective.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summing up
&lt;/h1&gt;

&lt;p&gt;As you can see, building with Flutter and Amplify lends itself really well to baking in best practice application architecture into your solution. In this article I showed you how to use Repository and BLoC patterns and how well they can facade the Amplify DataStore API calls.  This then makes it very easy to build out a data persistence mechanism for your application that will function in online and offline modes with little code.&lt;/p&gt;

&lt;p&gt;This was a very short example, if you want to see a more involved examples using multiple repositories and cubits - see &lt;a href="https://github.com/deekob/flutter_droidcon" rel="noopener noreferrer"&gt;My Github Repo&lt;/a&gt;. I have also written a &lt;a href="https://dev.to/awscommunity-asean/building-android-with-flutter-and-aws-amplify-part-1-25p3"&gt;Series of Amplify and Flutter&lt;/a&gt; posts detailing how to build out an application.&lt;/p&gt;

&lt;p&gt;As always, please help me with future articles by providing feedback.&lt;/p&gt;

&lt;p&gt;Until next time...&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>aws</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>10 Things I've learnt being an AWS Developer Advocate</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Mon, 30 Aug 2021 04:15:49 +0000</pubDate>
      <link>https://dev.to/deekob/10-things-i-ve-learnt-being-an-aws-developer-advocate-37ck</link>
      <guid>https://dev.to/deekob/10-things-i-ve-learnt-being-an-aws-developer-advocate-37ck</guid>
      <description>&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;So exactly 12 months ago I landed a job as a &lt;a href="https://twitter.com/riferrei/status/1425882512925069314?s=19"&gt;Developer Advocate&lt;/a&gt; at AWS. A job title, that when I left school with a rudimentary knowledge of Java v1 didn't exist.  Now this isn't unusual in the technology industry today, where new job roles and titles are popping up almost daily. The role of Developer Advocate(DA), is becoming more and more popular, so I want to share my experiences and the advice that I would give to anyone who wants to be a DA or indeed wants to work at AWS. As inevitably most of this blog is also about working at AWS, as well as, being in Developer Relations as a Developer Advocate. &lt;/p&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;My career has been primarily in technology as a software developer/engineer who is at their happiest in front of a screen building software. After 15 or so years of this I decided to try something different and joined AWS as a Solutions Architect (SA). Luckily enough a role came up in the most remote and beautiful of cities, Perth Western Australia where I live (see top of post for a pic). Three years into AWS I managed to land a role as a DA, due mostly to dumb luck and an understanding hiring manager.  &lt;/p&gt;

&lt;p&gt;This role I have found at times to be very challenging, however I have enjoyed it immensely. Mostly due to the fact that I am able to influence and help developers on a far greater scale than I could have previously hoped for as a software engineer. So here are the things I have learned and techniques I have adopted to help me be a better Developer Advocate at AWS, but still stay true to my software engineering roots.  As being a DA does mean that you work in marketing, a fact that on first glance would seem a bit strange.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 1: Internal Vs External Advocacy
&lt;/h1&gt;

&lt;p&gt;AWS is a large company and ships lots of code in the form of services and features to the platform. To be effective as a DA you need to be known within the organisation as a thought leader and someone who can deliver great content on a service or feature. This requires &lt;strong&gt;Internal Advocacy&lt;/strong&gt;. At the same time you also are employed to advocate on behalf of developers who are using AWS services and features to build cool products. This requires &lt;strong&gt;External Advocacy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Its easy to over index on one over the other, the most effective DA's always find the perfect balance of the two.  They are well known by teams within AWS but also by the external developers they advocate on behalf of. They have built this balance up over time, and have spent many hours building the relationships required to be successful, there is no short cut. Put simply, you cant advocate for developers unless you intimately know / can influence the services they are building with, so take the time to find that balance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 2: Take notes
&lt;/h1&gt;

&lt;p&gt;Writing things down is important, inside AWS writing takes on a whole new level of importance. Being a developer advocate inside AWS, good writing skills are &lt;strong&gt;essential&lt;/strong&gt;.  You are always writing; blogs, code, internal documents etc. The skill I would like to highlight, is the art of taking actionable notes. Notes that not only make sense as you write them, but also will make sense to future self and can be turned into actions. &lt;/p&gt;

&lt;p&gt;Take this scenario - you are thinking about your next great piece of content. An idea comes to you while walking for your morning coffee, you sketch out the idea in you head, it sounds awesome !!. You get home and start to write the idea down, only to find you have forgotten half the content you thought of on your walk, you end up not doing the content. &lt;strong&gt;DO NOT LET THIS HAPPEN!&lt;/strong&gt;* Always have a way of writing your ideas down.  Even if they simply act as a catalyst for other ideas, don't waste them - take your phone out &lt;strong&gt;WRITE IT DOWN&lt;/strong&gt;, but always take the time to write a coherent structured sentence. A sentence that you in four weeks time can read and take action from.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 3: Execute
&lt;/h1&gt;

&lt;p&gt;Thoughts and ideas are good, writing them down is a great step but actions and results are better. You can have 10 great ideas but unless they lead into 10 pieces of great content (blogs, streams, videos) they are meaningless. You need to execute on an idea - make it a reality, as that reality is what you will be ultimately judged on.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 4: Prioritise , Prioritise and Re-Prioritse
&lt;/h1&gt;

&lt;p&gt;Now there is no way you have time to execute 10 ideas - in fact you will be lucky to execute on a very small number. This is due many things, life, distractions, life and even distractions ;) - so make prioritising what you are going to execute on a constant. Ask yourself, is this the right thing to be concentrating on right now?  Will this have a better impact on the thing that is next on my list? Will it be worth the x number of days of focus to create it? There is no worse feeling than spending a week creating a blog post that no-one reads or gets little impact. This skill of prioritising and re-prioritising is also a non negotiable when working inside AWS, as work is always infinite, you need to keep prioritising!&lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 5: Become your own producer
&lt;/h1&gt;

&lt;p&gt;The modern DA needs to produce content in a number of formats.  No longer is it a case of showing up at a conference or meetup - speaking and conveying a message to a group who are physically in the same room as you.  Now it is a case of preparing and talking to a camera and conveying your message to a group of people you cant see, who might be on the other side of the globe. You  could be streaming a live coding session or recording a video showing how to build x - but you need to be as engaging as if you where on stage in front of your audience.&lt;/p&gt;

&lt;p&gt;In this arena production quality and setup is important, very important.  You need to cut through and engage people, your session needs to stand out and engage people quickly, so make sure you learn how to build up a YouTube or Twitch channel. Read about how others are doing it, find out what camera, mic, editing software is being used.  Learn from others like:  (in no particular order) &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/channel/UCSLIvjWJwLRQze9Pn4cectQ"&gt;Marcia&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/channel/UC7mca3O0DmdSG2Cr80sOD7g"&gt;Nader&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/channel/UCv75sKQFFIenWHrprnrR9aA"&gt;Kyle&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now recording is not something I had much experience with at all, and building a good set up is not easy,  you constantly underestimate the time involved. But that time needs to be invested so that the content you have does not get lost in the ever increasing amount of digital content available.&lt;/p&gt;

&lt;p&gt;That all being said, remember, &lt;strong&gt;good content is everything&lt;/strong&gt; Being a good producer just makes your content more accessible to a wider audience and makes it more likely that people will actually find and watch it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 6: Time and Timezone mismanagement
&lt;/h1&gt;

&lt;p&gt;I live on the GMT +8 timezone and work for a US based company that for the most part operates 12 hours behind me.  There are always meetings on at any time of the day or night. Do not feel obliged to turn up, give yourself a break from the 24 hour development cycle.  Set your own time and set your own time boundaries, you are the best qualified to know when these are.  Yes there will be times when you need to present at midnight to a group 8 hours time difference away.  Make this an exception, if you do not, you will not last, you will burnout and miss the opportunities that you are presented with, due simply to being over tired.  Learn the 'art of saying no' &lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 7: Be Authentic
&lt;/h1&gt;

&lt;p&gt;Be your true self, by that I mean, if you are a software developer, build content that is focused on code. If you love architectural principals, build content that is focused on architectural patterns and practices. I find the best content is that which you, the creator, would like to watch. So try and keep coding, and always be your authentic self with your audience. &lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 8:Have fun
&lt;/h1&gt;

&lt;p&gt;If you don't enjoy what you are doing, it comes through in the final product, so play to your strengths.  If you find yourself writing an article or recording a video and your heart is not in it, because you find the content boring and uninspiring, then STOP, DON'T GO ANY FURTHER. I have made this mistake many times, and what ultimately results is content that you are not proud of and ultimately you end up dumping anyway.  Save yourself some time and anguish :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 9: Reuse and Recycle
&lt;/h1&gt;

&lt;p&gt;Don't be afraid to take someone else's idea and content and put your own perspective on it, make it your own.  At first I was guilty of trying to think of unique content for everything, this quickly doesn't scale as how many truly unique things are left to do?  Instead, I was given great advice "its OK to reuse" and recycle - but put your own perspective and take on things".&lt;/p&gt;

&lt;p&gt;You may see an excellent you tube series that you think would make a great blog series or is missing an important addition. There are a few caveats of course - don't copy and plagiarize, instead, add something to existing content and always acknowledge the "shoulders of giants" you are standing on.  &lt;/p&gt;

&lt;h1&gt;
  
  
  Tip 10: Always seek feedback/review
&lt;/h1&gt;

&lt;p&gt;How can you ever hope to improve if you &lt;strong&gt;DONT&lt;/strong&gt; ask for feedback? the short answer is - you can't.&lt;/p&gt;

&lt;h1&gt;
  
  
  In Summary
&lt;/h1&gt;

&lt;p&gt;Working at AWS is a unique and humbling experience in and of itself. The culture , peculiarities and people, all make it different every single day.  Working as a Developer Advocate within the organisation, gives you the reach and the ability to really influence and affect change in the software solutions that developers are creating today.  Its a real privilege to help developers around the world, and in a very small way, build the software solutions of the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.jobs/en/search?offset=0&amp;amp;result_limit=10&amp;amp;sort=relevant&amp;amp;category%5B%5D=marketing-pr&amp;amp;distanceType=Mi&amp;amp;radius=24km&amp;amp;latitude=&amp;amp;longitude=&amp;amp;loc_group_id=&amp;amp;loc_query=&amp;amp;base_query=Developer%20Advocate&amp;amp;city=&amp;amp;country=&amp;amp;region=&amp;amp;county=&amp;amp;query_options=&amp;amp;"&gt;And we are always hiring&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Thanks for reading, as as always feedback always welcome - see Tip 10 &lt;/p&gt;

</description>
      <category>aws</category>
      <category>devrel</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>Building Android with Flutter and AWS Amplify — Part 3</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Tue, 10 Aug 2021 02:16:54 +0000</pubDate>
      <link>https://dev.to/awscommunity-asean/building-android-with-flutter-and-aws-amplify-part-3-5740</link>
      <guid>https://dev.to/awscommunity-asean/building-android-with-flutter-and-aws-amplify-part-3-5740</guid>
      <description>&lt;h3&gt;
  
  
  Building Android with Flutter and AWS Amplify — Part 3
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8mgDoInF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A1LMvY-_J5MGSkUpid480Fw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8mgDoInF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A1LMvY-_J5MGSkUpid480Fw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;This is final part of a three part series on using Flutter with AWS Amplify to build an Android mobile application. In &lt;a href="https://dev.to/deekob/building-android-with-flutter-and-aws-amplify-part-1-405-temp-slug-1885323"&gt;Part One&lt;/a&gt; we scaffolded out our shopping list application using Flutter to build our UI, before adding offline datasync capabilities to the application with &lt;a href="https://docs.amplify.aws/lib/auth/getting-started/q/platform/flutter"&gt;Amplify Datastore&lt;/a&gt;. Then in &lt;a href="https://dev.to/deekob/building-android-with-flutter-and-aws-amplify-part-2-37ib-temp-slug-6743577"&gt;Part Two&lt;/a&gt; we built a login / signup UI in Flutter and implemented the backend auth flow with &lt;a href="https://aws.amazon.com/cognito/"&gt;Amazon Cognito&lt;/a&gt; using the &lt;a href="https://docs.amplify.aws/lib/auth/getting-started/q/platform/flutter"&gt;Amplify Auth Category&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this the final part of the series we will complete the application by adding two additional features from our backlog:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the ability to store the users profile image in the cloud&lt;/li&gt;
&lt;li&gt;Add the ability to track user behaviour with analytics.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What are we going to build ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UxsQVy5z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/797/1%2AxnLkC8VyvkncZK8UBXyWpg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UxsQVy5z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/797/1%2AxnLkC8VyvkncZK8UBXyWpg.png" alt="the app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post we will start to build a screen flow that allows us to add an image to the user profile UI. Clicking &lt;strong&gt;‘Change Avatar’&lt;/strong&gt; will launch the &lt;strong&gt;Android Gallery&lt;/strong&gt; and allow us to choose an image, once an image is chosen we will be returned to the profile screen, with the chosen images displayed. As well as being displayed the image will be uploaded to the application back end, so that a user can maintain their profile image across devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the UI in Flutter
&lt;/h3&gt;

&lt;p&gt;As ever, the first step in the process is to build out the UI in Flutter. To build the ‘ &lt;strong&gt;profile image widget&lt;/strong&gt; ’ we will need two widgets, 1 / a circular profile widget to view the image in and, 2 / a button to launch the android gallery view so we can choose a new image. The UI code will look something like this&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;profile_view.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oEn02DhK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/701/1%2AU3gm_9qdUV1G1wtgiLgtfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oEn02DhK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/701/1%2AU3gm_9qdUV1G1wtgiLgtfw.png" alt="view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So here are the two widgets inside profile view — as you can see, we are using a BLoC to pass a state object into the UI. In this case the state object will store our image once we have grabbed it from the phones gallery. If we also look at the button code we can see that the call to open the gallery goes to the &lt;strong&gt;ChangeAvatarRequest()&lt;/strong&gt; method — lets have a look at that next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;profile_bloc.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zerKvGf3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/657/1%2AExtJPCQIPcTfhotP7riwnQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zerKvGf3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/657/1%2AExtJPCQIPcTfhotP7riwnQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So inside the Profile BloC’s &lt;strong&gt;mapEventToState()&lt;/strong&gt; method, if the &lt;strong&gt;ChangeAvatarRequest&lt;/strong&gt; event comes in we are doing a number of things.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;using an &lt;strong&gt;_imagePicker&lt;/strong&gt; to set the &lt;strong&gt;selectedImage&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;passing that image to a &lt;strong&gt;storageRepository&lt;/strong&gt; for uploading&lt;/li&gt;
&lt;li&gt;updating the user object with the imageKey to the image&lt;/li&gt;
&lt;li&gt;updating the user profile and grabbing the URL for the uploaded image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this to work we are missing a few things that we need to setup, namely the &lt;strong&gt;_imagepicker&lt;/strong&gt; and a &lt;strong&gt;storageRepository&lt;/strong&gt;. So lets do these next.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Adding an Image Picker&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;So the image picker is the package that will open the Android Gallery app and allow us to pick an image. This functionality we can get from Flutter’s repository of packages &lt;a href="https://pub.dev/"&gt;pub.dev&lt;/a&gt; , with the one we want being called &lt;a href="https://pub.dev/packages/image_picker"&gt;image_picker&lt;/a&gt;. If we navigate to the image_picker package on pub.dev we are given installation instructions for Android.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tb-O5K4g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/772/1%2AkRIiC1IlZuVvZPvMkWqlsA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tb-O5K4g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/772/1%2AkRIiC1IlZuVvZPvMkWqlsA.png" alt=""&gt;&lt;/a&gt;image_picker Android install instructions&lt;/p&gt;

&lt;p&gt;So all we need to do then to install &lt;strong&gt;image_picker&lt;/strong&gt; for use in our Android device is add it into pubspec.yml.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating a Storage Repository&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As in previous posts, we are using the repository pattern to wrap and abstract API calls for the underlying storage mechanism. In the Storage Repository’s case we are abstracting away the AWS Amplify Storage feature/category. So lets install that category before proceeding.&lt;/p&gt;

&lt;p&gt;To do this we run the following command from a terminal within our project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify add storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e49Ci0If--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/797/1%2A9y-u09HJe-iA09Dtu1jx1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e49Ci0If--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/797/1%2A9y-u09HJe-iA09Dtu1jx1w.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is then prompting us to fill in some values, we are choosing mostly defaults apart from the ‘Who should have access’ question where we select Auth users only and give them full CRUD privileges.&lt;/p&gt;

&lt;p&gt;Once we are happy with this, we call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to push all of these changes into the AWS account and provision an S3 bucket for our uploaded files to live in with the correct access controls built in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding dependencies and API calls&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The next thing we need to do is change ‘pubspec.yml’ to add the amplify_storage_s3 plugin before updating the_&lt;strong&gt;configureAmplify()&lt;/strong&gt; method in main.dart. This should now look like this with the added call to AmplifyStorageS3&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TNXjuSW0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/556/1%2AyL7wcjbeUNBqj45PQ7WqyA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TNXjuSW0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/556/1%2AyL7wcjbeUNBqj45PQ7WqyA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding the new libraries of &lt;strong&gt;image_picker&lt;/strong&gt; and &lt;strong&gt;amplify_storage_s3&lt;/strong&gt;  , our pubspec.yml should now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4bVD9rMR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/253/1%2AV9_m9ksdzg0dvesVMOc7Gw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4bVD9rMR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/253/1%2AV9_m9ksdzg0dvesVMOc7Gw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the libraries are added and our backend is provisioned, the final part is to wrap the storage API calls in a repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;storage_repository.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wKhf-XCl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/624/1%2AA1DxGRpBrvd30QFNg1hLRQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wKhf-XCl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/624/1%2AA1DxGRpBrvd30QFNg1hLRQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see this is similar to all the other repository’s in the application, for this storage repository was are wrapping the &lt;strong&gt;Storage.uploadFile()&lt;/strong&gt; and &lt;strong&gt;Storage.getURL()&lt;/strong&gt; methods in &lt;strong&gt;Amplify.Storage&lt;/strong&gt; api calls — Also notice that we are not re-downloading the file to view the Image, but rather using the Flutter &lt;strong&gt;NetworkImage&lt;/strong&gt; widget (see &lt;strong&gt;profile_view.dart&lt;/strong&gt; above)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Adding Analytics&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now in the final part of our journey we will add some analytics into our application so we can track how our users actually use the application in the wild. To do this we are going to implement a quick set of analytics to track user behaviour using &lt;a href="https://docs.amplify.aws/lib/analytics/getting-started/q/platform/flutter"&gt;Amplify’s Analytics&lt;/a&gt; category. We first need to add this to our project by following the usual steps, namely&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the terminal run ‘ &lt;strong&gt;amplify add analytics&lt;/strong&gt; ’ — and fill out the prompts as below&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ql6vFfsO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/812/1%2AVDZtlhOcEQbsqSrwBS1pnA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ql6vFfsO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/812/1%2AVDZtlhOcEQbsqSrwBS1pnA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In the terminal run ‘ &lt;strong&gt;amplify push -y&lt;/strong&gt; ’ — to deploy the changes to the back end. Like all other Amplify categories the Analytics category abstracts away a service from the developer, to reduce the complexity of provisioning. In the case of Analytics the service is &lt;a href="https://aws.amazon.com/pinpoint/"&gt;Amazon Pinpoint&lt;/a&gt; that we are provisioning as the back end Analytics service. You can also stream the Analytics events into an &lt;a href="https://aws.amazon.com/kinesis/"&gt;Amazon Kenesis&lt;/a&gt;stream and then implement another provider to act on these events in the stream.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, add the ‘ &lt;strong&gt;amplify_analytics_pinpoint&lt;/strong&gt; ’ dependency to pubspec.yml&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G00mtfAP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/303/1%2ASMZAuW385VUu_gGaU-hVCQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G00mtfAP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/303/1%2ASMZAuW385VUu_gGaU-hVCQ.png" alt=""&gt;&lt;/a&gt;pubspec.yaml snippet&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finally, update amplify configure code in main.dart, to include the new category of Analytics&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YrNBQAWg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/585/1%2AVub2PKxc1jvgUrrhLOC5bg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YrNBQAWg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/585/1%2AVub2PKxc1jvgUrrhLOC5bg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;| Note: If everything is done correctly then re-launching the app should work — if this doesn't work as expected, check the contents of &lt;strong&gt;amplifyconfiguration.dart&lt;/strong&gt; in this file are all the configuration details for the Amplify categories. If configuration values are not there for categories that are added to your project then the app will throw an exception on startup with details of what is missing.&lt;/p&gt;

&lt;p&gt;Assuming everything is working and the application is starting the next step is to decide on what event to capture. For the purposes of this app, we are going to be interested in shopping items as they are added to the &lt;strong&gt;Shopping List&lt;/strong&gt; by each user. A great feature of Amplify Analytics is that it can introspect the user details and easily add them to the event we want to track. So lets set that event up now&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;shoppinglistItem_view.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---MxaKi8---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/655/1%2AhXXR5eDgx5A_yeLA-Ayd8Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---MxaKi8---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/655/1%2AhXXR5eDgx5A_yeLA-Ayd8Q.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we are intercepting the UI call by the user to create the new item in their shopping list. The &lt;strong&gt;recordNewEvent&lt;/strong&gt; method will then decorate the event and send it to &lt;a href="https://aws.amazon.com/pinpoint/"&gt;Pinpoint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;shoppinglistItem_view.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--72V5PyIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/555/1%2A4JMQwGo7YlauIYXq4sj-aw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--72V5PyIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/555/1%2A4JMQwGo7YlauIYXq4sj-aw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This does some minor decoration before registering the event which by default will be flushed every 30 seconds to the Pinpoint servuce. This default can be changed inside &lt;strong&gt;amplifyconfiguration.dart&lt;/strong&gt; if desired. See &lt;a href="https://docs.amplify.aws/lib/analytics/record/q/platform/flutter"&gt;here&lt;/a&gt; for more information and details on the information we can capture using the Amplify Analytics category.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summing Up
&lt;/h3&gt;

&lt;p&gt;So that is it for our journey building out our Android Flutter application to store our shopping list data, in this final part we added functionality to upload our profile images and to track our user behaviour with analytics. We talked about the ability to pull in existing components from flutter pub.get to interact with native controls. Then we added the Storage category from Amplify to the project which provisioned an Amazon S3 bucket with the correct permission to store our applications images. Finally we added the Analytics category to provision an Amazon Pinpoint instance to store any events we want to track of user interaction on the device.&lt;/p&gt;

&lt;p&gt;During the course of this series we have seen how well Flutter and AWS Amplify can be used in tandem to really accelerate the application development and delivery lifecycle. So that ultimately you can deliver applications with great features into the hands of users quicker than ever.&lt;/p&gt;

&lt;p&gt;If you want to follow along with this blog and build this application yourself, remember the finished code can be cloned from &lt;a href="https://github.com/deekob/flutter_droidcon"&gt;https://github.com/deekob/flutter_droidcon&lt;/a&gt; and you can also follow along with Kyle (aka &lt;a href="https://twitter.com/Kilo_Loco"&gt;Kilo Loco&lt;/a&gt;) in his excellent #30daysofflutter &lt;a href="https://www.youtube.com/watch?v=rR3mxe6qcmw&amp;amp;list=PL5h37q2DJtAP36eI9j9yKxbZBASUMb9eZ"&gt;you-tube playlist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading, please reach out on twitter if you have any questions.&lt;/p&gt;

&lt;p&gt;Until next time…&lt;/p&gt;




</description>
      <category>android</category>
      <category>flutter</category>
      <category>aws</category>
      <category>androiddev</category>
    </item>
    <item>
      <title>Building Android with Flutter and AWS Amplify — Part 2</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Thu, 05 Aug 2021 14:26:04 +0000</pubDate>
      <link>https://dev.to/awscommunity-asean/building-android-with-flutter-and-aws-amplify-part-2-5h69</link>
      <guid>https://dev.to/awscommunity-asean/building-android-with-flutter-and-aws-amplify-part-2-5h69</guid>
      <description>&lt;h3&gt;
  
  
  Building Android with Flutter and AWS Amplify — Part 2
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ftmTc2d1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A3k0JnvzoMhkR8nCFMMDbcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ftmTc2d1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A3k0JnvzoMhkR8nCFMMDbcg.png" alt="Post Title"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This is Part two in a series of three articles describing how to build out an Android mobile application using &lt;a href="https://flutter.dev/"&gt;Flutter&lt;/a&gt; and &lt;a href="https://aws.amazon.com/amplify/"&gt;AWS Amplify&lt;/a&gt;. In &lt;a href="https://dev.to/deekob/building-android-with-flutter-and-aws-amplify-part-1-405-temp-slug-1885323"&gt;Part One&lt;/a&gt; we scaffolded out our shopping list application using Flutter to build our UI. We then, added our initial offline first datamodel using&lt;a href="https://sandbox.amplifyapp.com/getting-started"&gt;AWS Amplify AdminUI&lt;/a&gt; sandbox and tested that it worked with our UI. Then we deployed our changes and provisioned the AWS services that control our application’s backend ( &lt;a href="https://aws.amazon.com/appsync/"&gt;AWS Appsync&lt;/a&gt; and &lt;a href="https://aws.amazon.com/dynamodb/"&gt;Amazon DynamoDB&lt;/a&gt; ) and finally we made sure that all clients were correctly seeing the most up to date version of our shopping list data as it synchronized between them.&lt;/p&gt;

&lt;p&gt;In Part two we are going add extra features to our application by creating a user login screen, this will add the ability to login, signup and verify signup for our application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are we going to build ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uU96Wash--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/832/1%2A-OXLReu_bg6AoQlsRsdNoA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uU96Wash--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/832/1%2A-OXLReu_bg6AoQlsRsdNoA.png" alt="App Preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First Screen — “Login”&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enter Email
&lt;/li&gt;
&lt;li&gt;Enter Password ( hidden)
&lt;/li&gt;
&lt;li&gt;Submit
&lt;/li&gt;
&lt;li&gt;Link to Signup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Second Screen — “SignUp”&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enter UserName
&lt;/li&gt;
&lt;li&gt;Enter Email
&lt;/li&gt;
&lt;li&gt;Enter Password
&lt;/li&gt;
&lt;li&gt;Submit
&lt;/li&gt;
&lt;li&gt;Link to Login&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Third Screen — “Confirm Signup”&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enter email verification code
&lt;/li&gt;
&lt;li&gt;Submit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you can see what is a very standard login flow, prompting the user to login if they have an account. If they don't have an account then prompting the user to signup, with an additional step in signup to verify a code that has been sent to their signup email address.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the UI in Flutter
&lt;/h3&gt;

&lt;p&gt;So the first step as always is to flesh out the UI and how that will behave using dart. Again as with the previous article all the code for this demo you can find here &lt;a href="https://github.com/deekob/flutter_droidcon"&gt;https://github.com/deekob/flutter_droidcon&lt;/a&gt; . Now we know from the last post that in Flutter &lt;a href="https://flutter.dev/docs/resources/architectural-overview"&gt;everything is a widget&lt;/a&gt; so in this post I want to introduce another Flutter concept and that is that of &lt;strong&gt;Cubit&lt;/strong&gt; and &lt;strong&gt;BloC&lt;/strong&gt; patterns.&lt;/p&gt;

&lt;p&gt;In basic terms the &lt;a href="https://bloclibrary.dev/#/"&gt;&lt;strong&gt;BLoC&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(&lt;/strong&gt; Business Logic Components ) is a pattern that has come from the Flutter community, so if you haven't built anything in Dart, its unlikely you would have come across it. However if you have used patterns like Redux, MVVM and MVC it will be very familiar to you. In a nutshell what BLoC does is take a stream of &lt;strong&gt;Events&lt;/strong&gt; ( from the UI ) and transforms them to a stream of &lt;strong&gt;States — &lt;/strong&gt; with &lt;strong&gt;Cubit&lt;/strong&gt; being a subset of &lt;strong&gt;BloC&lt;/strong&gt; and relies on methods as opposed to events to emit state change events. As there are a few state changes in a normal authentication flow, it means handling these changes and updating your UI accordingly can get fiddly, but using Cubit with a repository pattern is a nice way of decoupling these state changes.&lt;/p&gt;

&lt;p&gt;So lets start by looking at the main login screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;login_view.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dvBF6vsw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/701/1%2A21De40-Vm5yFXjE4H9SOIQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dvBF6vsw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/701/1%2A21De40-Vm5yFXjE4H9SOIQ.png" alt="view.dart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So if we look at login view for example, we can see in the &lt;strong&gt;build&lt;/strong&gt; method we now introduce a &lt;strong&gt;context&lt;/strong&gt; , inside of which we have an &lt;strong&gt;AuthRepository&lt;/strong&gt; and an &lt;strong&gt;AuthCubit&lt;/strong&gt; ( more on those later ). This context is then passed to the UI if elements need access to it. We then also have our widgets defined and they can be wrapped in a &lt;strong&gt;BlocListener&lt;/strong&gt; which will be invoked when any state changes, so we can in effect update the UI based on state — which is exactly what we want for the auth flow. So lets now have a look at the &lt;strong&gt;LoginBloc&lt;/strong&gt; that is taking the events from the UI and updating the state accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;login_bloc.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y-M99SYo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/658/1%2AE9xhh7S1wxt_Za6Fm3WUNw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y-M99SYo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/658/1%2AE9xhh7S1wxt_Za6Fm3WUNw.png" alt="bloc.dart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main method in a Bloc is always the &lt;a href="https://pub.dev/packages/bloc"&gt;&lt;strong&gt;mapEventToState&lt;/strong&gt;&lt;/a&gt; method — which does exactly what you think it would do. As you can see we are listening for events from the UI and depending on their type we are updating the state. Depending on the event we may also want to make a call to the Auth repository ( as we do here with &lt;strong&gt;LoginSubmitted&lt;/strong&gt; ). The repository is how we are abstracting our auth store from our application. In &lt;a href="https://dev.to/deekob/building-android-with-flutter-and-aws-amplify-part-1-405-temp-slug-1885323"&gt;Part One&lt;/a&gt; we also used a repository pattern to do the same for our data store holding our shoppinglist data. Next lets see what our auth repository looks like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;auth_repository.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i3Fj7RCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/916/1%2ARXRJxGi8KWNvkGgocv8NhQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3Fj7RCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/916/1%2ARXRJxGi8KWNvkGgocv8NhQ.png" alt="repository"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see the repository is wrapping calls to the &lt;strong&gt;Amplify.Auth&lt;/strong&gt; library, much the same way as in the previous post our repository was wrapping calls to the &lt;strong&gt;Amplify.Datastore&lt;/strong&gt; library. So the next step is to update our application to add the Amplify Auth libraries and associated backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Amplify Auth
&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is update pubspec.yml and add the new Amplify Auth dependancy — after that has been done the dependancies section in the file should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pubspec.yml&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--131vKGAc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/320/1%2AhHMINocPnwdqrwvrMt5tXw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--131vKGAc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/320/1%2AhHMINocPnwdqrwvrMt5tXw.png" alt="pubspec.yaml"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice we also have the other plugins we have been using, &lt;strong&gt;flutter_bloc&lt;/strong&gt; , &lt;strong&gt;amplify_datastore&lt;/strong&gt; and a &lt;strong&gt;mplify_flutter&lt;/strong&gt; already there from &lt;a href="https://dev.to/deekob/building-android-with-flutter-and-aws-amplify-part-1-405-temp-slug-1885323"&gt;Part One&lt;/a&gt;. Once ‘pub get’ has been run we can now go ahead and change our amplify configure code in main.dart to configure the new Amplify feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;main.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6AyhNlD6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/583/1%2A_keNxhksRAUEvqCRfdAw_w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6AyhNlD6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/583/1%2A_keNxhksRAUEvqCRfdAw_w.png" alt="main.dart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will complete the code changes we need to do to configure the Amplify libraries, the final part is to configure and deploy the supporting backend. Just like the &lt;strong&gt;Amplify Datastore&lt;/strong&gt; feature/category abstracted &lt;a href="https://aws.amazon.com/appsync/"&gt;&lt;strong&gt;AWS AppSync&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://aws.amazon.com/dynamodb"&gt;&lt;strong&gt;Amazon DynamoDB&lt;/strong&gt;&lt;/a&gt;in &lt;a href="https://dev.to/deekob/building-android-with-flutter-and-aws-amplify-part-1-405-temp-slug-1885323"&gt;Part One&lt;/a&gt; of this series, the &lt;strong&gt;Amplify Auth&lt;/strong&gt; category abstracts &lt;a href="https://aws.amazon.com/cognito/"&gt;&lt;strong&gt;Amazon Cognito&lt;/strong&gt;&lt;/a&gt; as our identity provider (IDP) where our users are held. To provision this we can either use the Amplify CLI and call ‘amplify add auth’ or once again we can use the &lt;strong&gt;Amplify Admin UI.&lt;/strong&gt; In this example we will use the Admin UI, we do that by selecting ‘User Management’ in the Admin UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LPhGkZjd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/698/1%2AnYCq06cXGjZSuJnjC9hsIA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LPhGkZjd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/698/1%2AnYCq06cXGjZSuJnjC9hsIA.png" alt="AdminUI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;User Management allows us to choose which login mechanism we would like to use (email, username, etc.), if we would like to add MFA and how that would behave (SMS, Authenticator app). We can choose what attributes users must provide when signing up as well as configuring our password policy.&lt;/p&gt;

&lt;p&gt;In our example we will be using username as a login mechanism — so remove the default of email and replace it with Username.&lt;/p&gt;

&lt;p&gt;Once happy with our choices, selecting ‘Save and deploy’ will create a Amazon Cognito instance for our user store and add it into our backend. This also enables us to create users for our app via the Admin UI. Once that is complete and the backend has been deployed we then re-pull the configuration down locally like before — as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uz8SVtKE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/778/1%2AsKBcxIjdxt8kX5i5qBJ2Uw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uz8SVtKE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/778/1%2AsKBcxIjdxt8kX5i5qBJ2Uw.png" alt="local config"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the terminal in our local dev environment we simply run the ‘amplify pull’ command shown above to pull down all the backend configuration that we just created using thhe Amplify AdminUI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rinse and Repeat&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the login UI, states and actions all connected into our repository, the next stage is to do the same for sign-up, and confirm. This involves writing the view , BloC and states for those actions, much the same way we just did for the login action. Below is an excerpt from the confirm view&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;confirm_view.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P3FlOhFx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/596/1%2Ahu8-PcC9A4c9aC04IhniSg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P3FlOhFx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/596/1%2Ahu8-PcC9A4c9aC04IhniSg.png" alt="view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see its exactly the same pattern. Once we have completed these classes for sign-up and confirm — we simply make sure that the repository has the necessary &lt;strong&gt;Amplify.Auth&lt;/strong&gt; API calls and that should now be our functionality complete — you can see the calls in the &lt;strong&gt;Auth Repository&lt;/strong&gt; snippet below&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;auth_repository.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oOZIqyn---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/916/1%2AgOmF7XbueKJLXwuvT-mwaQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oOZIqyn---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/916/1%2AgOmF7XbueKJLXwuvT-mwaQ.png" alt="repo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summing Up
&lt;/h3&gt;

&lt;p&gt;So that is Part 2 of our Android Flutter application built, in this part we created the UI for login, signup and confirm screen flow. We talked about the &lt;strong&gt;BLoC&lt;/strong&gt; / &lt;strong&gt;Cubit&lt;/strong&gt; pattern in Flutter and how we can manage state changes with it. Then we added the &lt;strong&gt;Auth&lt;/strong&gt; category from Amplify to the project which provisioned &lt;a href="https://aws.amazon.com/cognito/"&gt;&lt;strong&gt;Amazon&lt;/strong&gt;  &lt;strong&gt;Cognito&lt;/strong&gt;&lt;/a&gt; and gave us a consistent Auth API which we wrapped in a repository on the client.&lt;/p&gt;

&lt;p&gt;In the next and final part of the series we will build out another screen in our application — this will hold the user profile information and we will learn how to implement image upload capabilities from our Android device so we can store our users picture as part of their profile in AWS.&lt;/p&gt;

&lt;p&gt;If you want to follow along with this blog and build this application yourself, remember the finished code can be cloned from &lt;a href="https://github.com/deekob/flutter_droidcon"&gt;https://github.com/deekob/flutter_droidcon&lt;/a&gt; and you can also follow along with Kyle (aka &lt;a href="https://twitter.com/Kilo_Loco"&gt;Kilo Loco&lt;/a&gt;) in his excellent #30daysofflutter &lt;a href="https://www.youtube.com/watch?v=rR3mxe6qcmw&amp;amp;list=PL5h37q2DJtAP36eI9j9yKxbZBASUMb9eZ"&gt;you-tube playlist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Until next time…&lt;/p&gt;




</description>
      <category>flutter</category>
      <category>aws</category>
      <category>android</category>
    </item>
    <item>
      <title>Building Android with Flutter and AWS Amplify — Part 1</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Tue, 03 Aug 2021 09:54:25 +0000</pubDate>
      <link>https://dev.to/awscommunity-asean/building-android-with-flutter-and-aws-amplify-part-1-25p3</link>
      <guid>https://dev.to/awscommunity-asean/building-android-with-flutter-and-aws-amplify-part-1-25p3</guid>
      <description>&lt;h3&gt;
  
  
  Building Android with Flutter and AWS Amplify — Part 1
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Az5GyBE9yGN-3mRXKlXZ8Iw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Az5GyBE9yGN-3mRXKlXZ8Iw.png" alt="Building Android with Flutter and AWS Amplify — Part 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;This is the first part of a three part series on using Flutter with AWS Amplify. In this series we will learn how to setup a Flutter project with Amplify, to make the most of the Amplify provided categories/features like auth, storage and API. In Part 1 we will go through how to scaffold a project and build out an initial UI, before creating our application’s data storage model and provisioning out the AWS cloud backend. In &lt;a href="https://proandroiddev.com/building-android-with-flutter-and-aws-amplify-part-2-ac247cdee619" rel="noopener noreferrer"&gt;part two&lt;/a&gt; and &lt;a href="https://proandroiddev.com/building-android-with-flutter-and-aws-amplify-part-3-e712cb195ba2" rel="noopener noreferrer"&gt;part three&lt;/a&gt; of this series we will build on this application and add additional features available in AWS Amplify, such as Auth, cloud storage and analytics.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Flutter ?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://flutter.dev/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt; was introduced by Google as an open source cross platform development environment for IOS and Android that uses Dart as its programming language. Flutter itself is relatively new( v1.0 was released December 2018 ). Since that time is has expanded beyond native mobile into web and desktop applications. At the time of writing, Flutter is gaining greater popularity than React Native on both GitHub and Stack Overflow due in part to its fantastic developer experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Whats is AWS Amplify ?
&lt;/h3&gt;

&lt;p&gt;First launched in late 2017, &lt;a href="https://aws.amazon.com/amplify/" rel="noopener noreferrer"&gt;AWS Amplify&lt;/a&gt; is a package of tools and services designed to make it easy for developers to create and launch apps in AWS, including code libraries, ready-to-use components, and a built-in CLI.&lt;/p&gt;

&lt;p&gt;For those who have not come across AWS Amplify before It is an opinionated framework that makes assumptions on the developer’s behalf, so less time on scaffolding boiler plate code and more time building features. In 2020 Amplify released a beta set of &lt;a href="https://github.com/aws-amplify/amplify-flutter" rel="noopener noreferrer"&gt;open source client libraries&lt;/a&gt;that support Flutter, these subsequently went GA in Feb 2021.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are we going to build ?
&lt;/h3&gt;

&lt;p&gt;In part 1 of this series we are going to build out an application that helps users to store the ‘humble’ shopping list. This shopping list application should store a list of items that a household would like to buy, and as members visit the shops they can tick off items from the list. The ultimate aim of ‘smart shopping list’ is that the chore of shopping can be shared ! . So lets look at the typical screen flow of our application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F948%2F1%2AtBuvwPrna1gMA4ZR7WrWmQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F948%2F1%2AtBuvwPrna1gMA4ZR7WrWmQ.png" alt="The Shopping List App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First Screen&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mark Item as complete (purchased)&lt;/li&gt;
&lt;li&gt;Add New Item&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Second Screen&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add new Item to List&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Third Screen&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View List&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A pretty straight forward flow, so let’s get started …&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring your Developer Environment
&lt;/h3&gt;

&lt;p&gt;Before we start building out the UI, we need to install and configure our development environment to run Flutter — this is beyond the scope of this blog, however just follow these steps to set up your local Flutter environment.&lt;/p&gt;

&lt;p&gt;For my IDE of choice I will be using VSCode, however Android Studio is also supported with setup instructions &lt;a href="https://flutter.dev/docs/get-started/install/macos#android-setup.%20https://flutter.dev/docs/get-started/editor?tab=androidstudio" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Once you have configured the editor, &lt;a href="https://flutter.dev/docs/get-started/test-drive?tab=androidstudio%20" rel="noopener noreferrer"&gt;test your configuration&lt;/a&gt; and then finally write a quick &lt;a href="https://flutter.dev/docs/get-started/codelab" rel="noopener noreferrer"&gt;hello world&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;| Note:&lt;/strong&gt; There are also some great tips and tricks for Android Developers in the flutter documentation &lt;a href="https://flutter.dev/docs/get-started/flutter-for/android-devs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building out the UI in Flutter
&lt;/h3&gt;

&lt;p&gt;Once we are all setup lets jump in and build our UI. All the code for this demo you can find here &lt;a href="https://github.com/deekob/flutter_droidcon" rel="noopener noreferrer"&gt;https://github.com/deekob/flutter_droidcon&lt;/a&gt; — The first thing to do is create a new Flutter Project — you can either do this at the command line using ‘flutter create’ or by using your IDE of choice.&lt;/p&gt;

&lt;p&gt;The entry point to any flutter project is main.dart — lets change that first to include our theme and styling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;main.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F907%2F1%2Az0wkMioEd2j5cXK6UKWqfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F907%2F1%2Az0wkMioEd2j5cXK6UKWqfw.png" alt="main.dart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is nothing more than the entry point to the application, where we can setup the UI theme before then calling our first View — In this case I have called the first view &lt;strong&gt;ShoppingListItemsView&lt;/strong&gt;  — which I will define in a new class file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;shoppinglist_view.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F702%2F1%2AZolKZEa1ccBClXNtyVJTvQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F702%2F1%2AZolKZEa1ccBClXNtyVJTvQ.png" alt="view.dart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the main part of our UI, its broken up into a number of Widgets, as everything in &lt;a href="https://flutter.dev/docs/resources/architectural-overview" rel="noopener noreferrer"&gt;Flutter is a widget&lt;/a&gt; — In this case we are using a &lt;strong&gt;StatefulWidget&lt;/strong&gt; to hold the state (Shopping List Item) that we are creating. The &lt;strong&gt;StatefulWidget&lt;/strong&gt; has a state that has one &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;/strong&gt; method called &lt;strong&gt;build(BuildContext context)&lt;/strong&gt; — and this is where we create our UI.&lt;/p&gt;

&lt;p&gt;The UI itself we have a &lt;strong&gt;_navbar&lt;/strong&gt; that simply holds the apps title ‘My Shopping List’, a &lt;strong&gt;_floatingActionButton&lt;/strong&gt; that when pressed, will push a modal from the bottom containing the widget called &lt;strong&gt;_newItemView&lt;/strong&gt;. Finally in this widget we have a &lt;strong&gt;TextEditingController&lt;/strong&gt; that holds the value of our new item and a button to save the item. Notice that the &lt;strong&gt;onPressed()&lt;/strong&gt; event in the &lt;strong&gt;ElevatedButton&lt;/strong&gt; isnt doing anything yet. Before we can persist anything however we first need to define our application data model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building our DataModel
&lt;/h3&gt;

&lt;p&gt;Now with the UI done we want to start thinking about persisting the application’s data. To do this we will use the &lt;strong&gt;Amplify Admin UI&lt;/strong&gt; to model what the data objects will be, what fields and types they contain, which fields are optional and which are mandatory. So, lets start by opening the Admin UI sandbox web page found at &lt;a href="https://sandbox.amplifyapp.com/start" rel="noopener noreferrer"&gt;AdminUI sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From here we get to choose what we are building, either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Application Backend&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hosting a Web Site&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ultimately, we want to build out an application backend for this application, so we choose the first option &lt;strong&gt;‘Create an app backend&lt;/strong&gt; ’.&lt;/p&gt;

&lt;p&gt;We can either start modelling the data layer, set up an auth layer or configure file storage for our application. In this case we are focusing on data, so lets choose ‘Data &amp;gt;&amp;gt;’&lt;/p&gt;

&lt;h3&gt;
  
  
  Modelling our data
&lt;/h3&gt;

&lt;p&gt;Next, we start to think about our data model, its relationships and how to test it locally. This can all be done when we select the ‘Data’ feature and then selecting the ‘Blank Schema’ option, as we want to start from scratch, finally hitting ‘Create New Schema’ to get going.&lt;/p&gt;

&lt;p&gt;With the Admin UI data modelling feature we can do things like choose the names and types for our data. For individual types we can decide if they are simple types like ‘String’ or more complex scalar types like AWSDateTime. Along with model / field names and types we can also mark the fields as mandatory by ticking the ‘isRequired’ checkbox, or mark them as being a collection by ticking the ‘is array’ checkbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F552%2F1%2Av_kCrqgk9ANHYjjIkzX1Hw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F552%2F1%2Av_kCrqgk9ANHYjjIkzX1Hw.png" alt="AdminUI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can easily define relationships between model elements, currently I can define a 1:1, 1:M and M:M relationships inside the AdminUI.&lt;/p&gt;

&lt;p&gt;The model we want to define is a ShoppingListItem &lt;strong&gt;must&lt;/strong&gt; have a name ( &lt;strong&gt;itemName&lt;/strong&gt; ) and a flag( &lt;strong&gt;isComplete&lt;/strong&gt; ) to let the user know if the item has been bought. This model will look something like this&lt;/p&gt;

&lt;p&gt;For more in depth instructions on how to get started with data modelling, check out &lt;a href="https://docs.amplify.aws/console/data/data-model" rel="noopener noreferrer"&gt;Amplify Docs&lt;/a&gt;. Next we can test this locally to see if it fits our understanding and the existing front-end code. Selecting ‘Test this Locally’ brings up a set of steps we need to perform to integrate this new model into the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Amplify to our app
&lt;/h3&gt;

&lt;p&gt;The steps on how to build and test our newly created schema are nicely laid out for us as a series of steps in the AdminUI, so let’s step through these. The first one looks like this :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F466%2F1%2AatksNx-Os_YxPYdf7wSfbQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F466%2F1%2AatksNx-Os_YxPYdf7wSfbQ.png" alt="Amplify AdminUI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the type of App we are building — we can choose either a Web, IOS / Android or cross- platform and then make the appropriate framework/language selection based on our platform selection. For this example, we are choosing a cross platform ( even though we will be targeting Android ) with Flutter as the framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 (Optional)
&lt;/h3&gt;

&lt;p&gt;This step will contain the commands needed if you are creating an app from scratch. As we are using an existing frontend UI, we will skip this step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3
&lt;/h3&gt;

&lt;p&gt;The next step contains the commands needed to install the Amplify CLI, which we then use to pull down the data model we have created in the AdminUI Sandbox. This ‘amplify pull’ command will also in our case generate the correct types that we can use to access our data model in our Dart code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4
&lt;/h3&gt;

&lt;p&gt;This step shows us how to install the Amplify Flutter libraries that we can use to interact with the types pulled down and generated in Step 3. It also shows us how to configure our application to start implementing Amplify client libraries in our frontend code and finally how to configure the native projects for IOS and Android. In summary what we will do is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add the dependencies to &lt;strong&gt;pubspec.yaml&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Pub Get&lt;/strong&gt; in the “Flutter commands” bar to install Amplify Libraries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(iOS only) Open &lt;strong&gt;ios/Podfile&lt;/strong&gt; and update the platform target:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go to &lt;strong&gt;main.dart&lt;/strong&gt; and add the following lines to initialize the Amplify Libraries:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;main.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F640%2F1%2ABjInEfbj6l6Q_PqwmvT0zg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F640%2F1%2ABjInEfbj6l6Q_PqwmvT0zg.png" alt="main.dart again"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5
&lt;/h3&gt;

&lt;p&gt;Finally, this step shows us how to build CRUD operations into our application by supplying code snippets for Amplify Datastore. Datastore is an Amplify category/feature that enables applications to be built with a client first programming model. The beauty of the datastore category is that once we deploy a backend for our app into an AWS account, the data persistence mechanisms we have defined in datastore will mean all application data will automatically be synced to all connected clients. This is due to the service that underpins datastore, AWS Appsync.&lt;/p&gt;

&lt;p&gt;For a more in-depth view of what datastore can do -&amp;gt; &lt;a href="https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js" rel="noopener noreferrer"&gt;check this out&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, for the ShoppingListItem model, if I want to persist items into our local datastore we would use the datastore API and code something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F646%2F1%2AFipEKWJtIXY9LylTWGeJxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F646%2F1%2AFipEKWJtIXY9LylTWGeJxg.png" alt="DataStore API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The datastore API is pretty elegant, in that with this line of code we are storing the result in your local sqllite DB. But before we check to see if this is working, lets explore how we would code the other CRUD operations. To do this we create a data repository class to abstract all of the data operations in our app. This class look like this&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;shoppinglist_repo.dart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F683%2F1%2APNvvnNjAfDCr0sqfEE-ppA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F683%2F1%2APNvvnNjAfDCr0sqfEE-ppA.png" alt="repository"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that the args for the updateListItem function have a bool? — this is because the &lt;strong&gt;isComplete&lt;/strong&gt; field is non-mandatory and Dart is inherently null safe as are the&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/07/amplify-flutter-now-supports-null-safety/" rel="noopener noreferrer"&gt;Amplify client libs&lt;/a&gt;. — To find out more about Dart null safety&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Locally
&lt;/h3&gt;

&lt;p&gt;So now we have modelled our data in AdminUI, installed the Amplify CLI, pulled down the required libraries and model schema from the AdminUI sandbox, generated types in Dart for our model AND changed our application to call the datastore API for these types. We have done a lot in a short time, the last thing we need to do is test it all works. So we can simply run our application and excercise the creating of shopping list items. As mentioned previously all the items created will be stored only on the device&lt;/p&gt;

&lt;h3&gt;
  
  
  Whats Next ?
&lt;/h3&gt;

&lt;p&gt;If we are not happy with our model, we can go back to our sandbox and re-model our data again. We can re-run the &lt;strong&gt;‘amplify pull&lt;/strong&gt; ’ command to retrieve any changes to our model and re-generate the types accordingly inside our codebase. This way we can quickly iterate on our model until it is fit for purpose.&lt;/p&gt;

&lt;p&gt;When we are finally happy with the application and the data that it is persisting, we can move on to connecting our AWS account up and start to think about adding other features like datasync, authentication, content management and user management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying to an AWS Account
&lt;/h3&gt;

&lt;p&gt;Deploying the app into a AWS account is the final step of the Sandbox Admin UI process and can be kicked off by selecting ‘Deploy to AWS’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F879%2F1%2AyMQSczIhvjs8SmA9nDURWA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F879%2F1%2AyMQSczIhvjs8SmA9nDURWA.png" alt="Deploying"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are then prompted to either deploy into a new AWS account or deploy to an existing account. If we choose a new account, we will be taken to the account creation wizard and stepped through the process of creating a brand-new AWS account. However, in this case, lets assume we will use an existing AWS account and so we are subsequently prompted to login to that account in the browser.&lt;/p&gt;

&lt;p&gt;Once successfully logged in on the browser, the next step takes us to the AWS console where we give our new backend a name.&lt;/p&gt;

&lt;p&gt;Once the app has been given a name and region click on ‘ &lt;strong&gt;Confirm Deployment&lt;/strong&gt; ’ will begin the deployment process of the backend into our AWS account. This may take a few minutes to execute…&lt;/p&gt;

&lt;p&gt;Once the deployment has completed, the next step is to open the Admin UI page for our app and start adding additional features to the backend. To do this simply click on ‘Open Admin UI’&lt;/p&gt;

&lt;p&gt;Once the AdminUI is open, you will see that it looks very different than it did as a &lt;a href="https://sandbox.amplifyapp.com/getting-started" rel="noopener noreferrer"&gt;Sandbox&lt;/a&gt;, this is because now with the addition of an AWS account there is a large array of features we can add that were not available in the sandbox like authorization and storage, that I will cover in later posts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating local dev
&lt;/h3&gt;

&lt;p&gt;Before we can do anything, we need to connect our local development environment to our newly deployed backend. To do this we need to find the ‘amplify pull’ command to run locally. To find out what it is, click on the ‘Local Setup Instructions’ link as shown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F879%2F1%2A69GUOU4HFZ4M5Hj_mam6oQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F879%2F1%2A69GUOU4HFZ4M5Hj_mam6oQ.png" alt="local dev"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify pull -appId [xxxxxxxx] -envName staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will then kick off an authentication challenge, once that successfully completes we will then be prompted to set up our local amplify project so it is ‘in-sync’ with our backend. The main things it will synchronise are the cloudformation templates that describe the backend services and generate any types from the objects created in the data model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Application Content
&lt;/h3&gt;

&lt;p&gt;Next thing we will do is to test that our data model is deployed correctly and that syncing updates to our app works as expected. To do this we can use a feature of AdminUI called ‘App Content Management’.&lt;/p&gt;

&lt;p&gt;App Content Management allows us to create data in our back-end for testing but also it allows us to create markdown content. For our purposes we will create some test &lt;strong&gt;ShoppingListItems&lt;/strong&gt; using the built-in editor that we will then use to test data sync. We will create two items for our Shopping List [&lt;strong&gt;Cheese&lt;/strong&gt; , Crackers ]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F879%2F1%2A_c3PcSq_Uv69MnB--awdIw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F879%2F1%2A_c3PcSq_Uv69MnB--awdIw.png" alt="it works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the items are created, we can start up our app in the local dev environment and ❗ boom ❗ without any code changes at all we have both of these &lt;strong&gt;ShoppingListItems&lt;/strong&gt; displaying in the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summing Up
&lt;/h3&gt;

&lt;p&gt;So that's it for Part 1 in this series on Amplify and Flutter on Android — In this post we have explored how to set up our initial project, we have built our UI using Flutter, installed the Amplify tools and data modeled and tested our application data using Amplify Admin UI Sandbox. We did all this without having an AWS account, as we used one of Amplify’s categories/features called Datastore which allows us to store application data locally on the device.&lt;/p&gt;

&lt;p&gt;We then deployed our solution to an AWS account, this enabled us to test the persistence of the data in the cloud. With Amplify provisioning the backend of AWS Appsync and Amazon DynamoDB which was abstracted from the developer by the Amplify Datastore category. Another real advantage of using Datastore is that we were then able to sync data to the cloud and other connected clients without changing any of the api calls, as Amplify Datastore takes care of all of this heavy lifting for us.&lt;/p&gt;

&lt;p&gt;As you can see using Amplify and Flutter together are a great way to accelerate your application development cycle on Android, in Part 2 of this series we will add login and signup to our application and start building the concept of a user profile.&lt;/p&gt;

&lt;p&gt;See you then …&lt;/p&gt;




</description>
      <category>android</category>
      <category>aws</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Adding image upload to a React Native app with AWS Amplify</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Tue, 04 May 2021 05:12:56 +0000</pubDate>
      <link>https://dev.to/aws/adding-image-upload-to-a-react-native-app-with-aws-amplify-4j7m</link>
      <guid>https://dev.to/aws/adding-image-upload-to-a-react-native-app-with-aws-amplify-4j7m</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;In this blog post I will detail how to leverage AWS Amplify, AWS Appsync and Amazon S3 to provide an image upload capability into a react native app. &lt;/p&gt;

&lt;p&gt;This blog assumes that the reader is familiar with AWS Amplify and its feature categories, however if you are not familiar then please have a read of this &lt;a href="https://dev.to/aws-builders/aws-amplify-2l30"&gt;great intro&lt;/a&gt; by @ajonp one of our AWS community members.&lt;/p&gt;

&lt;p&gt;I will also be working off an existing AWS backend application that has already been created with the AWS Amplify CLI and has had both the 'auth' and 'api' feature categories added.  You can follow along with this creation by going to the official &lt;a href="https://docs.amplify.aws/start/q/integration/react-native" rel="noopener noreferrer"&gt;Amplify react-native docs&lt;/a&gt;. Or ;tldr it and simply &lt;a href="https://github.com/deekob/amplify-react-native-storage" rel="noopener noreferrer"&gt;clone this repo&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note - if you do end up cloning the repo, remember to run 'amplify configure' and 'amplify init' to re-configure the project to a new cloud back end in your own AWS account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What are we building ?
&lt;/h2&gt;

&lt;p&gt;The base application we will be adding photo/image storage to should look something like the below image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfbfqbaoclk9rkmxva9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfbfqbaoclk9rkmxva9j.png" alt="Alt Text" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are two views in this app &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A list of Todo's that have already been created. &lt;/li&gt;
&lt;li&gt;A form to create a new Todo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clicking on 'New To Do' button on the 'list view' will  bring you to the 'create view' and once a new todo has been created the app will navigate back to the 'list view' from the 'create view'&lt;/p&gt;

&lt;p&gt;So, nothing too tricky here, what we are going to add to this app is the ability to save images to our cloud backend when in the 'create view' and the ability to see those images in the 'list view'.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the image concept to backend resources.
&lt;/h2&gt;

&lt;p&gt;First thing is confirming what amplify feature categories we have installed - to find this out we simply run the &lt;code&gt;amplify status&lt;/code&gt; command which gives the following output&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2r060khirq7hoavla7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2r060khirq7hoavla7r.png" alt="Amplify status" width="688" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we have both auth and api feature categories installed - so that means that there is an Amazon Cognito instance handling users and auth, an AWS Appsync instance handling the GraphQL API and an Amazon DynamoDB instance handling data persistence.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding an S3 storage bucket.
&lt;/h2&gt;

&lt;p&gt;First thing is to add the new Storage feature category to this existing backend. This is super easy to do by running the &lt;code&gt;amplify add storage&lt;/code&gt; command in the terminal and selecting the following options &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcgkvv4l1m27m0tcdgpop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcgkvv4l1m27m0tcdgpop.png" alt="Alt Text" width="800" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note - most of these are defaults apart from the 'Who should have access' question where I have selected Auth users only and given them full CRUD privilege.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Adding an 'image' to our API definition
&lt;/h2&gt;

&lt;p&gt;The next step is to add the concept of an image to our GraphQL schema definition. You can find the schema definition  under &lt;code&gt;./amplify/backend/api/myapi/schema.graphql&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We simply add a new String type called image - which will hold our image name when we persist an image and an image pre-signedURL when we retrieve an image. The schema should now look like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkt2qkmusq8wnv5dppd3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkt2qkmusq8wnv5dppd3v.png" alt="Alt Text" width="250" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: if we want to add FGAC to any of the fields in the schema we could use the '&lt;a class="mentioned-user" href="https://dev.to/auth"&gt;@auth&lt;/a&gt;' transformer directive - for more detail on access control, transformer directives and &lt;a class="mentioned-user" href="https://dev.to/auth"&gt;@auth&lt;/a&gt; [read this article] &lt;a href="https://aws.amazon.com/blogs/mobile/graphql-security-appsync-amplify/" rel="noopener noreferrer"&gt;https://aws.amazon.com/blogs/mobile/graphql-security-appsync-amplify/&lt;/a&gt; - by Brice Pelle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once that has been updated, we go back to our terminal and run &lt;code&gt;amplify update api&lt;/code&gt; command to regenerate the api definition.  The output from this command should look similar to the below: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44zmy7x32kj1ohrbkegl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44zmy7x32kj1ohrbkegl.png" alt="Alt Text" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once complete we finally need to call &lt;code&gt;amplify push --y&lt;/code&gt; to push all of our local changes into our cloud back end. This means that out back-end services will now look like the following architectural diagram with the addition of the S3 bucket that the Amplify CLI Storage category has provisioned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2e2kgd4tzyzc3de77mot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2e2kgd4tzyzc3de77mot.png" alt="Alt Text" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Updating the react native client application
&lt;/h2&gt;

&lt;p&gt;So that is enough messing around in the terminal, now it’s time to write some code.  We will be making all the changes in a single file App.js and the first thing we will do is add in some additional state to store the image name and the imageURI we want to add to each todo.  So, something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialTodoState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialAppState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;showForm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;imageURI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note - All of the code for the finished app can be found &lt;a href="https://github.com/deekob/amplify-react-native-storage/tree/completed" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Selecting Images
&lt;/h2&gt;

&lt;p&gt;Once we have that we need to write a function that locates images on the device and displays them for the user to select.  Luckily there is a third-party library called &lt;a href="https://github.com/react-native-image-picker/react-native-image-picker" rel="noopener noreferrer"&gt;React Native Image Picker&lt;/a&gt; that will do most of this for us. So, let’s create a function to call this library, it should look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;handleChoosePhoto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;launchImageLibrary&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;mediaType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;photo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;includeBase64&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;updateAppState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;imageURI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_todoPhoto.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="nf"&gt;updateTodoState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;handleChoosePhoto&lt;/code&gt; function is calling the &lt;code&gt;launchImageLibrary&lt;/code&gt; function, that is part of react-native-image-picker's api. We  are simply passing in some options like, image type , image height and image width and from the response we get the imageURI ( the location on the image on the device ) and we save this and  a unique image name in the state fields we created earlier.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: We a using the react-native-uuid library to make sure all images have unique names - this makes it easier when we store these names in DynamoDB.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When implemented the 'handleChoosePhoto' UI looks like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2089sce5eli27lj561gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2089sce5eli27lj561gu.png" alt="Alt Text" width="356" height="408"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Uploading Images
&lt;/h2&gt;

&lt;p&gt;Once we select the image, we can now create a Todo in the 'create view' that contains the image we have selected by simply adding the following tag in the App.js 'render' function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which should look something like this after adding in the appropriate  tag.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzyeg57yexri1si60un7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzyeg57yexri1si60un7.png" alt="Alt Text" width="394" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We next need to implement the function that is called by the 'Create Todo' button. This &lt;code&gt;addTodo()&lt;/code&gt; function is where we add the image upload code to the existing GraphQL api call as shown below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// new code for images&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appstate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imageURI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;photoBlob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;photoBlob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="c1"&gt;//existing api code&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;todoState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;graphqlOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
      &lt;span class="nf"&gt;setTodoState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialTodoState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;setAppState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialAppState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error creating todo:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are fetching the blob of the photo we have selected using the stored imageURI and then passing the result along with the unique image name to the Amplify &lt;code&gt;Storage.put()&lt;/code&gt; call. Not forgetting as we are using a new Amplify category of 'Storage' we need to import the relevant libraries at the top of App.js in the imports section&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphqlOperation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-amplify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have stored the blob in S3 using &lt;code&gt;Storage.put()&lt;/code&gt; the last part of the function persists the image name along with all the other todo data in DynamoDB via the AppSync GraphQL api call, before finally re-setting any state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downloading Images
&lt;/h2&gt;

&lt;p&gt;So now we have our image uploaded as part of the ‘create view’, the final step is to view the images when our initial 'list view' displays. It is currently looking very empty &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyd3c5ljzmorwbf48sp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyd3c5ljzmorwbf48sp8.png" alt="Alt Text" width="352" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To implement this, we first of all add in an image tag to the UI that renders the list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;      &lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we update the &lt;code&gt;fetchTodos&lt;/code&gt; function to retrieve the relevant image for every Todo we have in our list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//fetch the recipes from the server&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;graphqlOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listTodos&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listTodos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;
      &lt;span class="c1"&gt;// for all todos get the pre-signURL and store in images field&lt;/span&gt;
      &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;
      &lt;span class="nf"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;setTodoState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialTodoState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;setAppState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialAppState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error fetching todos &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that is it!!  your app should now look like this, where the image is downloaded into the app from the S3 bucket using the pre-signed URL that &lt;code&gt;Storage.get()&lt;/code&gt; so conveniently generates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fze5bhf9jx85ahs6rr87t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fze5bhf9jx85ahs6rr87t.png" alt="Alt Text" width="395" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So that is it, in a few minutes we have added an image upload and download feature into our existing react native application.  &lt;/p&gt;

&lt;p&gt;All we needed to do is update our backend using the Amplify CLI to provision our S3 bucket and change the schemas of our DynamoDB table and Appsync API. Then using the Amplify client libraries write a Storage.put() function and a Storage.get() function to add and retrieve the images. The result was not only fast to achieve, but also came with security built in.&lt;/p&gt;

&lt;p&gt;Thanks for reading, hopefully this makes adding this kind of feature to your react native application a breeze - if you would like me to go into more detail on any of the features of AWS Amplify or indeed anything AWS related in a future post - just post in the comments below.&lt;/p&gt;

&lt;p&gt;Likewise, any feedback is always welcome either here or catch me on the socials.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>aws</category>
      <category>cloudnative</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Building and Managing your Cloud Backend with Amplify Admin UI</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Thu, 25 Feb 2021 08:54:48 +0000</pubDate>
      <link>https://dev.to/aws/building-and-managing-your-cloud-backend-with-amplify-admin-ui-29k</link>
      <guid>https://dev.to/aws/building-and-managing-your-cloud-backend-with-amplify-admin-ui-29k</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;This is the second post in a series on the AWS Amplify Admin UI. In the &lt;a href="https://dev.to/aws/datamodel-your-react-app-with-aws-amplify-admin-ui-46o"&gt;first post&lt;/a&gt;  we explored how to use Admin UI to build and test a data model for a react app without needing to sign up for an AWS Account.  &lt;/p&gt;

&lt;p&gt;In this second post we will look at how Admin UI can be used as a 'single pane of glass' into your application's backend, to help speed up development, management and deployment of an application on AWS. We will see how to connect our application into an account, enabling us to add more advanced features like AuthN/AuthZ and datasync/client side notifications. Also learning how you can delegate access to develop application backends without having AWS accounts themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  ;tldr
&lt;/h2&gt;

&lt;p&gt;I've created a video going over all the steps in both blog articles. If you want to follow a step by step guide or just dont want to read, then check it out&lt;/p&gt;

&lt;p&gt;YouTube:   &lt;iframe src="https://www.youtube.com/embed/G_HqdNcIjuA"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to an Account
&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://dev.to/aws/datamodel-your-react-app-with-aws-amplify-admin-ui-46o"&gt;first post&lt;/a&gt; you'll remember that we left &lt;a href="https://github.com/deekob/Summit2021Demo" rel="noopener noreferrer"&gt;our application&lt;/a&gt; as local only, following the Amplify Admin UI wizard we were then able to build and test locally. Deploying the app into a AWS account is the final step of this process and can be kicked off by selecting 'Deploy to AWS'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3h08wqc4ey1ghd2bvqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3h08wqc4ey1ghd2bvqc.png" alt="Alt Text" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are then prompted to either deploy into a new AWS account or deploy to an existing account. If we choose a new account, we will be taken to the account creation wizard and stepped through the process of creating a brand-new AWS account.  However, in this case we will use an existing AWS account and so we are subsequently prompted to login to that account in the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsseplbi8kbdvlm60mk5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsseplbi8kbdvlm60mk5g.png" alt="Alt Text" width="758" height="714"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once successfully logged in on the browser, the next step takes us to the AWS console where we give our new backend a name.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❗ all screen shots that are in 'dark mode' are taken from the AWS Console, to help differentiate them from AWS Admin UI screens which will still be in 'light mode'❗&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the app has been given a name and region click on 'Confirm Deployment' will begin the deployment process of the backend into our AWS account.  This may take a few minutes to execute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbi4qkzh4l9ri0pdizk48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbi4qkzh4l9ri0pdizk48.png" alt="Alt Text" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note - under the region text box you will see 'Service Categories being Deploy' and AWS AppSync and DynamoDB mentioned. This is because we are using the Datastore category for CRUD operations in our app and these services underpin the Datastore category.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the deployment has completed, the next step is to open the Admin UI page for our app and start adding additional features to the backend. To do this simply click on 'Open Admin UI' &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxb7nsp3pq1un54n6tym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxb7nsp3pq1un54n6tym.png" alt="Alt Text" width="565" height="770"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the AdminUI is open, you will see that it looks very different than it did as a &lt;a href="https://sandbox.amplifyapp.com/getting-started" rel="noopener noreferrer"&gt;Sandbox&lt;/a&gt;, this is because now with the addition of an AWS account there is a large array of features we can add that were not available in the sandbox like authorisation, which I will cover later in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to local dev
&lt;/h2&gt;

&lt;p&gt;Before we can do anything, we need to connect our local development environment to our newly deployed backend.  To do this we need to find the 'amplify pull' command to run locally. To find out what it is, click on the 'Local Setup Instructions' link as shown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwl5qv9v20lrsytdrt59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwl5qv9v20lrsytdrt59.png" alt="Alt Text" width="778" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now running&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;amplify pull --appId [xxxxxxxx] --envName staging&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 will then kick off an authentication challenge, once that successfully completes we will then be prompted to set up our local amplify project so it is 'in-sync' with our backend.  The main things it will synchronise are the cloudformation templates that describe the backend services and generate any types from the objects created in the datamodel. &lt;/p&gt;

&lt;p&gt;Running the command will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzof7ltnzr603smy68q1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzof7ltnzr603smy68q1.png" alt="Alt Text" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have selected defaults for most of the questions, however it is worth noting the last question : "Do you plan on modifying this backend ( Y/n )" - selecting Y will mean that you can use the Amplify CLI locally to also add features to this backend.  For more information on adding features using the CLI see [here].(&lt;a href="https://docs.amplify.aws/cli" rel="noopener noreferrer"&gt;https://docs.amplify.aws/cli&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;We have now connected our backend environment with our local developer environment so let's start building.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding Application Content
&lt;/h2&gt;

&lt;p&gt;Next thing we will do is to test that our data model is deployed correctly and that syncing updates to our app works as expected. To do this we can use a feature of AdminUI called 'App Content Management'.  &lt;/p&gt;

&lt;p&gt;App Content Management allows us to create data in our back-end for testing but also it allows us to create markdown content.  This is particularly useful to allow app admins to manage content (e.g., update a product price or add a new blog post). You can work with basic data types (strings, integers, Booleans, etc..) as well as rich text using the built-in rich text editor for editing content for websites and blogs.&lt;/p&gt;

&lt;p&gt;For our purposes we will create some test ShoppingListItems using the built-in editor that we will then use to test data sync. We will create two items for our Shopping List [&lt;strong&gt;Cheese&lt;/strong&gt;, Crackers ] &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fajest1kvw3d4b5hucr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fajest1kvw3d4b5hucr.png" alt="Alt Text" width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the items are created, we can start up our app in the local dev environment and ❗ boom ❗ without any code changes at all we have both of these ShoppingListItems displaying in the app.  Now it is time to add an observer to our lists to make sure that any updates to the ShoppingList or ShoppingListItems in the list are correctly synched to all clients. &lt;/p&gt;

&lt;p&gt;This again we can do very simply by using the DataStore api:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;DataStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ShoppingListItems&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beauty of Datastore is its ability to abstract away the complexity of data persistence and synchronisation. With Datastore we can not only persist data and register observers on that data, we can also be selective in what data we would like synced back to clients. This is because Datastore is built as a client first programming model, in that, developers only need to code for local persistence.  With the GraphQL schema and client subscriptions provided by AWS Appsync abstracted away from you by the Datastore API’s handling the rest.  So that when the app becomes connected to a backend, the code you wrote will quite happily sync data with all connected clients without it being changed.&lt;/p&gt;

&lt;p&gt;More info on this operation and others available can be found in the excellent [Amplify Datastore Docs].(&lt;a href="https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js" rel="noopener noreferrer"&gt;https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Auth
&lt;/h2&gt;

&lt;p&gt;We have now implemented a full data sync engine between all of our application clients, the next stage is to secure this API and add Sign In / Sign Up capabilities to our UI.  We do that by selecting 'User Management' in the Admin UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qa014ntd8r9vqy096xo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qa014ntd8r9vqy096xo.png" alt="Alt Text" width="698" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;User Management allows us to choose which login mechanism we would like to use (email, username, etc.), if we would like to add MFA and how that would behave (SMS, Authenticator app). We can choose what attributes users must provide when signing up as well as configuring our password policy. Once happy with our choices, selecting 'Save and deploy' will create a Amazon Cognito instance for our user store and add it into our backend. This also enables us to create users for our app via the Admin UI.&lt;/p&gt;

&lt;p&gt;Now the backend is complete with can go perform the changes to our UI.  These changes are all described in detail &lt;a href="https://docs.amplify.aws/ui/auth/authenticator/q/framework/react" rel="noopener noreferrer"&gt;here&lt;/a&gt; - however, in a nutshell we will use one of the Higher Order Components(HOC) that comes with the AWS Amplify JS React library and wrap our app with that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;withAuthenticator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now users will have to Authenticate before being allowed to navigate to the main page of our app and call the backend API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwblcmn5ly1nw0001fz1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwblcmn5ly1nw0001fz1.png" alt="Alt Text" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Admin UI Management
&lt;/h2&gt;

&lt;p&gt;The next set of functionality I will go through is accessed through the AWS Amplify console for the application.  On the left hand menu there is a new option called 'Admin UI Management'&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb919bxjty7d6bfe3fkf0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb919bxjty7d6bfe3fkf0.png" alt="Alt Text" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under this heading you can enable/disable Admin UI for the app, see a list of all backend environments and the final thing we will go through in this post, controlling access to Admin UI. &lt;/p&gt;

&lt;h2&gt;
  
  
  Controlling Team Access
&lt;/h2&gt;

&lt;p&gt;The next thing we will do to our app backend is decide what members of our team can work on which aspect of the applications backend. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note - the following process is done vis the Amplify Console for our app and &lt;em&gt;not&lt;/em&gt; the Admin UI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ryjwcj0yzczqvxfctqn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ryjwcj0yzczqvxfctqn.png" alt="Alt Text" width="649" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have two types of scope that we can grant access to: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Full Access - this will give access to all features of the Admin UI and will allow developers to create and update features within the application backend.&lt;/li&gt;
&lt;li&gt;Manage Only - this will give access to a subset of features of the Admin UI , namely Content and User Management.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Depending on the scope granted to each user will mean changes to visibility of the following items.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faodm6h0qdtyrgb01a8xo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faodm6h0qdtyrgb01a8xo.png" alt="Alt Text" width="354" height="848"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/aws/datamodel-your-react-app-with-aws-amplify-admin-ui-46o"&gt;first part&lt;/a&gt; of this series we explored how we can use Amplify Admin UI &lt;em&gt;without&lt;/em&gt; an AWS account to quickly iterate through a data model design and test it locally. We then extended this concept in this post and showed how we can quickly and easily build out backend functionality for our application and &lt;em&gt;even&lt;/em&gt; grant access for developers &lt;em&gt;without&lt;/em&gt; an AWS account.  This in itself onlocks a whole new world of possibilty of how you build out your application within an organisation, by removing this account bottleneck the teams agility should only increase.&lt;/p&gt;

&lt;p&gt;The single pane of glass over the entire application backend is a very powerful feature of the Amplify Admin UI. To be able to have one single point where you can add/remove developers, change what features you have available inside your app, like authentication and authorisation and even change the content displayed. In my opinion makes it well worth checking out.  &lt;/p&gt;

&lt;p&gt;Thanks for reading, if you would like me to go into more detail on any of the features of Amplify Admin UI or indeed anything AWS related in a future post - just post in the comments below.  &lt;/p&gt;

&lt;p&gt;Likewise, any feedback is always welcome.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudnative</category>
      <category>javascript</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Datamodel your React app with AWS Amplify Admin UI</title>
      <dc:creator>Derek Bingham ☘️</dc:creator>
      <pubDate>Wed, 17 Feb 2021 12:44:29 +0000</pubDate>
      <link>https://dev.to/aws/datamodel-your-react-app-with-aws-amplify-admin-ui-46o</link>
      <guid>https://dev.to/aws/datamodel-your-react-app-with-aws-amplify-admin-ui-46o</guid>
      <description>&lt;p&gt;AWS Amplify Admin UI &lt;a href="https://aws.amazon.com/blogs/aws/aws-amplify-admin-ui-helps-you-develop-app-backends-no-cloud-experience-required/" rel="noopener noreferrer"&gt;was announced&lt;/a&gt; just prior to AWS re:invent 2020, the killer feature for me was the ability to start building out the data persistence model for an application without needing access to an AWS Account.  So in this blog post I will take you through how to add a data model and a persistence store to an existing React application and how you can go about testing it locally.&lt;/p&gt;

&lt;p&gt;For those who have not come across AWS Amplify before it is a great way to start dipping your toe into the full stack cloud development experience if you have not already. It is an opinionated framework that makes assumptions on the developer's behalf, so less time on scaffolding boiler plate code and more time building features. Also, the Amplify documentation is (in my opinion) the best documentation you'll find for an AWS service. Don't take my word for it - &lt;a href="https://docs.amplify.aws/" rel="noopener noreferrer"&gt;see for yourself&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the first in a two part series, if your more of a visual learner then check out &lt;a href="https://dev.to/aws/building-and-managing-your-cloud-backend-with-amplify-admin-ui-29k"&gt;part 2&lt;/a&gt; where I have included a video detailing all the steps I took for the demo. &lt;/p&gt;

&lt;h1&gt;
  
  
  What are we building?
&lt;/h1&gt;

&lt;p&gt;For this blog we will work with an app that already has had the bones of the UI &lt;a href="https://github.com/deekob/Summit2021Demo/tree/main/src" rel="noopener noreferrer"&gt;built&lt;/a&gt; in. The apps purpose is to collect information about shopping lists, allowing a user to &lt;strong&gt;Create Read Update&lt;/strong&gt; and &lt;strong&gt;Delete&lt;/strong&gt; items in a shopping list.  Now with the existing UI done we want to start thinking about persisting the application's data.  To do this we will use the Amplify Admin UI to model what the data objects will be, what fields and types they contain, which fields are optional and which are mandatory.  &lt;/p&gt;

&lt;h1&gt;
  
  
  Getting Started
&lt;/h1&gt;

&lt;p&gt;So lets go ahead and open the &lt;a href="https://sandbox.amplifyapp.com/start" rel="noopener noreferrer"&gt;AdminUI sandbox&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;From here we get to choose what we are building, either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Application Backend&lt;/li&gt;
&lt;li&gt;Hosting a Web Site&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ultimately, we want to build out an application backend for this application, so we choose the first option 'Create an app backend'. You will then be presented with the following options:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fftbvg0bdwug6wpdof3pa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fftbvg0bdwug6wpdof3pa.png" alt="Choose your Feature" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can either start modelling the data layer, set up an auth layer or configure file storage for our application. In this case we are focusing on data, so lets choose 'Data &amp;gt;&amp;gt;'&lt;/p&gt;

&lt;h1&gt;
  
  
  Modelling our data
&lt;/h1&gt;

&lt;p&gt;Next, we start to think about our data model, its relationships and how to test it locally. This can all be done when we select the 'Data' feature and then selecting the  'Blank Schema' option, as we want to start from scratch, finally hitting  'Create New Schema' to get going.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgbp9nnyp53esx2e7clm5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgbp9nnyp53esx2e7clm5.png" alt="DataTypes" width="580" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the Admin UI data modelling feature we can do things like choose the names and types for my data. For individual types we can decide if they are simple types like 'String' or more complex scalar types like AWSDateTime.  Along with model / field names and types we can also mark the fields as mandatory by ticking the 'isRequired' checkbox, or mark them as being a collection  by ticking the 'is array' checkbox.  Finally we can easily define relationships between model elements, currently I can define a 1:1, 1:M and M:M relationships inside the AdminUI.   &lt;/p&gt;

&lt;p&gt;The model we want to define is,  a ShoppingList which can have 1:M ShoppingListItems -&amp;gt; so, modelling that in the AdminUI looks something like this.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo0nms2pat8b7m25a1ket.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo0nms2pat8b7m25a1ket.png" alt="DataModel" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more in depth instructions on how to get started with data modelling, check out &lt;a href="https://docs.amplify.aws/console/data/data-model" rel="noopener noreferrer"&gt;Amplify Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we can test this locally to see if it fits our understanding and the existing front-end code. Selecting 'Test this Locally' brings up a set of steps we need to perform to integrate this new model into the application.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note - We can also share this model with other developers in the team as well as make it a template to base other models on. The model will exist in this sandbox until it is deployed into an AWS account, at which stage it will be locked down.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Adding Amplify to our app
&lt;/h1&gt;

&lt;p&gt;The steps on how to build and test our newly created schema are nicely laid out for us as a series of steps in the AdminUI, so let's step through these&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 1
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijcc6cmuq4t3m6mhxj16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijcc6cmuq4t3m6mhxj16.png" alt="PlatformChoice" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
Choose the type of App we are building - we can choose either a Web, IOS or Android platform and then make the appropriate framework/language selection based on our platform selection. For this example, we are choosing a Web platform with React as the framework.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 2 (Optional)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0j1n5z2t9zkwkf45ka5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0j1n5z2t9zkwkf45ka5s.png" alt="ScaffoldApp" width="800" height="253"&gt;&lt;/a&gt;&lt;br&gt;
This step will contain the commands needed if you are creating an app from scratch.  As we are using an existing frontend UI, we will skip this step.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 3
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqf7deeb9kbdbhdl01s9o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqf7deeb9kbdbhdl01s9o.png" alt="InstallCLI" width="800" height="313"&gt;&lt;/a&gt;&lt;br&gt;
The next step contains the commands needed to install the Amplify CLI, which we then use to pull down the data model we have created in the AdminUI Sandbox. This 'amplify pull' command will also in our case generate the correct types that we can use to access our data model in our React code.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 4
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqf7deeb9kbdbhdl01s9o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqf7deeb9kbdbhdl01s9o.png" alt="Install Amplify" width="800" height="313"&gt;&lt;/a&gt;&lt;br&gt;
This step shows us how to install the Amplify typescript library that we can use to interact with the types pulled down and generated in Step 3. It also shows us how to configure our application to start implementing Amplify libraries in our frontend code.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 5
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjchajzuc1yvmr1vwtmnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjchajzuc1yvmr1vwtmnp.png" alt="CRUD" width="800" height="430"&gt;&lt;/a&gt;&lt;br&gt;
Finally, this step shows how to build CRUD operations into our application by supplying code snippets for dataStore.  Datastore is an Amplify category/feature that enables applications to be built with a client first programming model.  The beauty of the datastore category is that once we deploy a backend for our app into an AWS account, the data persistence mechanisms we have defined in datastore will mean all application data will automatically be synced to all connected clients. This is due to the service that underpins datastore, AWS Appsync.&lt;/p&gt;

&lt;p&gt;For a more in-depth view of what datastore can do -&amp;gt; &lt;a href="https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js" rel="noopener noreferrer"&gt;check this out&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, for the ShoppingListItem model, if I want to persist items into our local datastore we would use the datastore API and code something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;DataStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShoppingListItems&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;itemName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;itemName&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The datastore API is pretty elegant, in that with this line of code we are storing the result in indexedDB in your local browser.   But before we check to see if this is working, lets explore how we would code the other CRUD operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//READ&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;DataStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ShoppingListItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//CREATE&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;DataStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShoppingListItems&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;itemName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;itemName&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//DELETE&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deleteItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modelToDelete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;DataStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ShoppingListItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;DataStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modelToDelete&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Testing Locally
&lt;/h1&gt;

&lt;p&gt;So now we have modelled our data in AdminUI, installed the Amplify CLI, pulled down the required libraries and model schema from the AdminUI sandbox, generated types in typescript for our model AND changed our application to call the datastore API for these types.  We have done a lot in a short time, the last thing we need to do is test it all works.&lt;/p&gt;

&lt;p&gt;As mentioned before, when calling datastore API's we actually persist the data in IndexedDB in the browser, which makes it super easy to test.  We can simply spin up the application, create some test data by executing the app's functionality.  Then, if we want to make sure the data is persisting in the format expected, we can inspect it using the development tools of the browser and query indexedDB as shown below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9cxzqkjql82zdtnfwf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9cxzqkjql82zdtnfwf6.png" alt="Testing the Application" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note - If, we are building an Android our IOS application, datastore would instead persist the data in SQLite database on the device as opposed to indexedDB in a browser.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Whats Next
&lt;/h1&gt;

&lt;p&gt;If we are not happy with our model, we can go back to our sandbox and re-model our data again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amplify pull --sandboxId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this command once more will retrieve any changes to our model and re-generate the types accordingly inside our codebase. This way we can quickly iterate on our model until it is fit for purpose.&lt;/p&gt;

&lt;p&gt;When we are finally happy with the application and the data that it is persisting, we can move on to connecting our AWS account up and start to think about adding other features like datasync, authentication, content management and user management.&lt;/p&gt;

&lt;p&gt;I will cover how this is done in a later blog.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;As you can see Amplify AdminUI is a great way to start modeling and iterating on our datamodel that we want to use within our application.  As there is no AWS account involved its very easy to just dive in and get started and the feedback cycle is really fast.  This enables a faster application development cycle and and helps aid faster delivery of our application into the hands of real users.&lt;/p&gt;

&lt;p&gt;So dont put it off any longer, dive in to the &lt;a href="https://docs.amplify.aws/console/adminui/start" rel="noopener noreferrer"&gt;Amplify AdminUI&lt;/a&gt;  and start building that next killer app.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/aws/building-and-managing-your-cloud-backend-with-amplify-admin-ui-29k"&gt;next post&lt;/a&gt; I will describe how we take this application to the next phase of development by deploying it to an AWS account and adding more advanced features like auth and datasync/notifications. I'll also show you how you can delegate access to developers who dont have AWS accounts themselves.&lt;/p&gt;

&lt;p&gt;Also, If there is specific content you want to see around this or other AWS topics, please get in touch. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>javascript</category>
      <category>awsamplify</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
