Flutter User Onboarding using ShowCaseView

Flutter User Onboarding using ShowCaseView

I have used Intro.js in the past to help onboard new users on React.js and Angular applications. This feature allows us to walk the user through the features in our app. While searching for a flutter equivalent I found a similar library on pub.dev, ShowCaseView:


showcaseview | Flutter Package

I will create a small example app demonstrating this. First, let’s create a blank Flutter app. I will be using Android Studio but you may also use Visual Studio Code.

We will use the default counter template it gives us:
{% gist https://gist.github.com/cmcoffeedev/426f4f977519969cebd0efe72ba70416.js %}
First, let’s add the ShowCaseView package as a library using this command in your terminal:

flutter pub add showcaseview

We will “install” the package using this command in the terminal:

flutter pub get

For simplicity we are going to use the default code the template gives us. In the stateless MyApp widget, wrap the home parameter of the Material App to with ShowcaseWidget.

home: ShowCaseWidget(
  builder: Builder(
      builder: (context) =>
          const MyHomePage(title: 'Flutter Demo Home Page')),
),

Note for this package to work correctly, the parent widget must use ShowCaseWidget.

Next lets wrap the floating action button and the counter text with “Showcase” widgets. The “Showcase” widget takes a key and description. Add these variables below the _counter variable.

final GlobalKey _floatingActionKey = GlobalKey();
final GlobalKey _counterKey = GlobalKey();

final String _incrementDescription = "Press this button to increment the count";

final String _counterTextDescription = "This text shows how many times you have pressed the button";

Next, let’s wrap the counter text with the Showcase widget:

Showcase(
  key: _counterKey,
  description: _counterTextDescription,
  child: Text(
    '$_counter',
    style: Theme.*of*(context).textTheme.headline4,
  ),
),

Now, let’s wrap the floating action button with a showcase widget:

floatingActionButton: Showcase(
  key: _floatingActionKey,
  description: _incrementDescription,
  child: FloatingActionButton(
    onPressed: _incrementCounter,
    tooltip: 'Increment',
    child: const Icon(Icons.*add*),
  ),
),

The following code will start the tutorial for the user:

ShowCaseWidget.*of*(context)
          ?.startShowCase([_floatingActionKey, _counterKey]));

Notice we are passing the keys in the order we want the widgets to be hightlighted.

Let’s put this in a function:

void _startTutorial(){
  ShowCaseWidget.*of*(context)?.startShowCase([_floatingActionKey, _counterKey]);
}

If we want to start this tutorial automatically, we must add this code to initState like so:

@override
void initState() {
  super.initState();
  WidgetsBinding.*instance*?.addPostFrameCallback((_) => _startTutorial());
}

Alternatively, to start this on a button click. Let’s create a TextButton under the counter Showcase widget:

TextButton(
    onPressed: _startTutorial,
    child: const Text("Restart Tutorial")
)

This is the full source code:

import 'package:flutter/material.dart';
import 'package:showcaseview/showcaseview.dart';

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ShowCaseWidget(
        builder: Builder(
            builder: (context) =>
                const MyHomePage(title: 'Flutter Demo Home Page')),
      ),
    );
  }
}

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

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  final GlobalKey _floatingActionKey = GlobalKey();
  final GlobalKey _counterKey = GlobalKey();

  final String _incrementDescription =
      "Press this button to increment the count";
  final String _counterTextDescription =
      "This text shows how many times you have pressed the button";

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

  void _startTutorial() {
    ShowCaseWidget.of(context)
        ?.startShowCase([_floatingActionKey, _counterKey]);
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((_) => _startTutorial());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Showcase(
              key: _counterKey,
              description: _counterTextDescription,
              child: Text(
                '$_counter',
                style: Theme.of(context).textTheme.headline4,
              ),
            ),
            TextButton(
                onPressed: _startTutorial,
                child: const Text("Restart Tutorial"))
          ],
        ),
      ),
      floatingActionButton: Showcase(
        key: _floatingActionKey,
        description: _incrementDescription,
        child: FloatingActionButton(
          onPressed: _incrementCounter,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}


Run the app and this should be your result:

Finished Demo for this project

Thank you for taking the time to read this article!

React.js Onboarding Using Intro.js

React.js Onboarding Using Intro.js

A demonstration of the basic usage of Intro.js

While working on an open-source project (Care Amarillo), I was looking for a way to guide users that are new. I’ve used Intro.js before so I searched and found a React wrapper for it. I will demonstrate the basic usage of this package.
intro.js-react

Here is a small example of what we want to achieve:

First, let’s create a blank react app. I am using create-react-app to set up boilerplate code. If you don’t have it already you can refer to the docs here:
GitHub – facebook/create-react-app: Set up a modern web app by running one command.

Run the command in the terminal (Make sure you are in the root directory of this project):

npx create-react-app onboarding

Note “onboarding” is the name I gave the React app.

After that finishes building you can run the app using this command in the terminal:

npm start

You will see the initial template like so:

Next open App.js.

The code initially is this:

import logo from './logo.svg';
import './App.css';
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit src/App.js and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;

Let’s change it to this:

import logo from './logo.svg';
import './App.css';
function App() {
  return (
    <div className="App">
      <div id="buttonRow">
        <button id="help">Help</button>
        <button id="about">About</button>
        <button id="contact">Contact Us</button>
      </div>
    </div>
  );
}
export default App;

Notice we removed the header element and added a row of buttons.

Next, open the App.css file and add the buttonRow CSS:

#buttonRow{
  display: flex;
  justify-content: space-evenly;
  padding: 8px;
}

Next, we need to install the intro.js react package. Run this command in your terminal:

npm i intro.js-react

Open App.js again and add these two imports:

import { Steps } from 'intro.js-react';
import React, {useState} from 'react';

Now right above buttonRow add the Intro.js Step component:

<Steps
    enabled={enabled}
    steps={steps}
    initialStep={initialStep}
    onExit={onExit}
/>

The full HTML should be this:

<div className="App">
    <Steps
        enabled={enabled}
        steps={steps}
        initialStep={initialStep}
        onExit={onExit}
     />
     <div id="buttonRow">
        <button id="help">Help</button>
        <button id="about">About</button>
        <button id="contact">Contact Us</button>
     </div>
</div>

Since App.js is a functional component we will use hooks for enabled, steps, and initialStep. Add this code right above the code that returns your HTML.

const [enabled,setEnabled] = useState(true)
const [initialStep,setInitialStep] = useState(0)

const onExit = () => {
    setEnabled(false)  
}
const steps = [
    {
      element: '#help',
      intro: 'You can use this button for help',
      position: 'right',
    },
    {
      element: '#about',
      intro: 'You can use this button to get more information',
    },
    {
      element: '#contact',
      intro: 'You can use this button to contact us',
    },
];

We’ve set enabled to true and initialStep to 0. This will automatically make Intro.js whenever the user gets to this screen. The onExit function disables the Intro.js after we exit.

The steps object defines our steps. In our case, we use id’s but the package supports other selectors as well. Intro defines the message we want for each step.

Reload the page and this should be your result:

Here is the full source code for App.js :

import './App.css';
import 'intro.js/introjs.css';
import { Steps } from 'intro.js-react';
import React, {useState} from 'react';
function App() {
    const [enabled,setEnabled] = useState(true);
    const [initialStep,setInitialStep] = useState(0);

    const onExit = () => {
        setEnabled(false)
    }

    const steps = [
        {
            element: '#help',
            intro: 'You can use this button for help',
            position: 'right',
        },
        {
            element: '#about',
            intro: 'You can use this button to get more information',
        },
        {
            element: '#contact',
            intro: 'You can use this button to contact us',
        },
    ];

    return (
        <div className="App">
            <Steps
              enabled={enabled}
              steps={steps}
              initialStep={initialStep}
              onExit={onExit}
            />
            <div id="buttonRow">
                <button id="help">Help</button>
                <button id="about">About</button>
                <button id="contact">Contact Us</button>
             </div>
        </div>
     );
}

export default App;

Thank you for taking the time to read this article!

How To Set Up a Free Business Email Using Your Domain

How To Set Up a Free Business Email Using Your Domain

Setting up a professional email will help you stand out with your career and business. So instead of chris@gmail.com, I can use chris@mycompanyname.com. I personally have a professional email that I use on my portfolio site and LinkedIn. This also helps keep my personal email separate and a little safer.

I’ll show you how to create a professional email using Zoho. They are a well-known company with a suite of business applications. They give you 5 free emails starting out and you just pay $1 per extra email after that.

Setting Up Your Domain With Zoho Mail

Of course, if you don’t have a domain, you will need one to get started. I purchase my domains using https://namecheap.com. Zoho also allows you to purchase a domain after signing up.

Click the link below to get started using Zoho:
Email Sign up – Create a new email account with Zoho Mail

You will need to fill out this form and use your phone number or email to verify your account.

Next, verify your account:

You will get a welcome screen after verifying. You can click on the “Add Now” button if you already have a domain or “Buy Now” to buy one. I’ll assume you have bought one so click Add Now.

Click the Proceed to domain verification button.

Zoho can usually detect where your domain is hosted. I have my domain pointed to Digital Ocean, so it tries to help me out. Either way, the steps are the same across the board.

You can visit this link for specific instructions on where to find where to copy these values here:
Domain Verification – Email Hosting with Zoho

I’ll walk through Digital Ocean, but it will be the same thing to copy and paste across the board.

I’ve found on Digital Ocean where I need to create records. I chose the TXT tab to add a TXT record and added the value I copied. For the hostname using “@” (without quotes of course).

I will click “Create Record” to create my TXT record. Now we need to go back to Zoho and click the “Verify TXT record” button.

You will now choose your administrator email. You can use your name, “info”, “support”, etc.

Click the “Create” button. You can add more users if you would like. Let’s go to DNS Mapping by clicking next on the “Setup Users” and “Setup Groups” pages.

You see we need to add 3 MX records, and 2 TXT Records. Let’s work from the bottom up.

We need to add a TXT record. First copy :

zmail_domainkey

Now set this as the host in Digital Ocean. You will set the value in the Value field.

Now let’s copy:

v=spf1 include:zoho.com ~all

This is the last TXT Record to add. Set the host name as “@” (without quotes”).

Now let’s add all three MX records. In Digital Ocean we can click on the MX tab.

Now you can click “Verify All Records”. Depending on the TTL you set when adding the records, it may take a while to verify some of them. I usually keep the defaults when using Digital Ocean.

You also do not have to wait to verify the records. The emails will work without staying on this page. They may also ask you to verify on the desktop when you log in, but not necessary.

If you do choose to wait for verification, you will get this message.

Congratulations, you have set up your first professional email!

Join Medium with my referral link. Your membership fee directly supports Christopher Coffee and other writers you read. You’ll also get full access to every story on Medium.
Join Medium with my referral link – Christopher Coffee

Host Your Website For Free Using Firebase

Host Your Website For Free Using Firebase

What is Firebase Hosting?

You can host your static websites for free using a Google service known as Firebase Hosting. Static websites include using React.js, Angular, and Vue.js. You can also of course host vanilla Javascript, HTML, and CSS projects. They also give you a free firebase domain that you can not alter. You can also use your domain.

It is also only free up to a certain usage per day, but the cost is low after that. Starting out you might not have as much traffic, so this will save you money early on.
Firebase Pricing

Firebase Hosting Limitations

You will not be able to host backend code on Firebase such as Node.js, Go, etc. You would also not be able to host a WordPress site on Firebase Hosting. Google does have other options that let you host these types of applications.

Getting Started

First, you need to download the firebase command-line tools. Don’t worry it’s simple. You don’t need to be a command-line expert. Also using an IDE such as Visual Studio Code or Webstorm will make this easier, as you can use the terminal directly in these applications.

Windows

For Windows you can download the CLI binary:

https://firebase.tools/bin/win/instant/latest

Linux, UNIX, and macOS

For Linux, UNIX, and macOS (is UNIX I believe) users, you can open your terminal and enter this command:

curl -sL https://firebase.tools | bash

Install using NPM

You can also install the tool using npm. You will need Node.js installed on your computer. You can install it here:
Download | Node.js

You will now need to install firebase tools. You only have to do this once. You can skip this step for future websites you may host.

In your terminal/command line app enter this command:

npm install -g firebase-tools

Then press Enter.

Logging in

On your Windows or Mac machine, type this in the terminal and press enter:

firebase login

This will open your browser to log in to your google account. You may also create a new Google account for your website.

After you log in, go back to your terminal and you will see a success message.

Creating a New Project on the Firebase Console

You will now need to create a new project on the Firebase Console. Go to this link below:
Sign in – Google Accounts

Now click “Add Project”

Now give your project a name and click Continue:

You can choose to enable Google Analytics for your project and click Continue.

If you enable Google Analytics you will need to specify a Google Analytics account or create a new one.

Now click Create Project.

This may take a while to complete.

Now click on the hosting tab on the left:

Click the **Get Started **button.

Next, it shows a screen of instructions to follow.

It will ask if you want to see steps on adding the Firebase Javascript SDK. We are going to skip this step.

Now you will need to initialize your firebase project on your computer. If you already have a website you want to upload, go to the terminal and go to the directory of the root of your website project. You will then enter:

firebase init

It will ask you many questions.

You will enter yes to the Is this a single-page app/website? question if your website is React, Angular, etc.

Also, enter **no **when it asks you to overwrite index.html

Click **Continue **on Firebase Hosting until you get to **Register your app. **You will need to specify a name here.

Click Register and continue

Next, we will add the Firebase SDK to your project. You can use npm if you are using React, Angular, Vue, etc. You can also use the script tag on any project. You will copy the script tag to the bottom of your index.html file above the closing tag.

Now deploy your website by entering this in the terminal and pressing Enter.

firebase deploy

It will show you the URL of your newly hosted website!

Go back to the firebase console and click Continue to console.

You can add your custom domain by clicking on the Add custom domain button and following the steps.

Congratulations on your first firebase hosted website!

Join Medium with my referral link. Your membership fee directly supports Christopher Coffee and other writers you read. You’ll also get full access to every story on Medium.
Join Medium with my referral link – Christopher Coffee

FlutterFlow Stepper Form with Firebase Firestore

FlutterFlow Stepper Form with Firebase Firestore

Introduction

I will show you how to create a custom widget in FlutterFlow to show a Stepper widget with form fields. We will submit the data to Firestore. This code is also reusable in non FlutterFlow generated apps.

You may copy and paste the code, but I want to walk through the thought process of how I would implement this for educational purposes. We will create an app saves the user’s grocery list.

Custom development

I get a lot of requests for custom tweaks of Google Maps with Flutter.

To better understand everyone’s needs, I have created a survey to capture these requests.

https://coffeebytez.substack.com/survey/639443

I will focus on the most highly requested items first, which will be available on my substack. I will also post exclusive in-depth tutorials there.

Please subscribe to my substack here:

https://coffeebytez.substack.com/?r=gdmkm&utm_campaign=pub-share-checklist

If you have urgent specific requests, please leave your contact information in the survey.

Creating the Form Field

First, we will use the FlutterFlow UI builder to create a TextFormField. You may also write the code yourself. In the blank home page of your project. Drag and drop a form field and style it how you want. I will also add the hint text.

Creating the Firestore Collection

Now let’s create the Firestore Collection in FlutterFlow. I am assuming you have already followed FlutterFlow documentation on connecting your firebase project.

Click on the Firestore tab, then click Create Collection. It will ask if you want to use a template, and you can choose the product template. I have deleted some fields.

Creating the Custom Widget

Now let’s create the custom form widget. Click the Custom Function tab on the left, then click the Custom Widget tab within that screen. Click the purple Create button. You should now see this screen. Name the widget and click View Boilerplate Code.

This will show another popup. Click Copy to Editor.

Click Save.

Go back to the TextField you created. Right-click on the TextField from the **Widget Tree **and click Copy Widget Code.

Go back to the custom widget you created and click the edit button (pencil icon). Above the build function, we will create another function that returns our form.

 @override
 Widget build(BuildContext context) {

Create a function that takes a textController and hint text. Type return, then copy and paste the code you copied earlier. The decoration code may be different depending on how you styled your code.

 Widget getTextFormWidget(
      TextEditingController textController, String customHintText) {
    return TextFormField(
      controller: textController,
      autofocus: true,
      obscureText: false,
      decoration: InputDecoration(
        hintText: customHintText,
        hintStyle: FlutterFlowTheme.of(context).bodyText2,
        enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0x00000000),
            width: 1,
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(4.0),
            topRight: Radius.circular(4.0),
          ),
        ),
        focusedBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0x00000000),
            width: 1,
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(4.0),
            topRight: Radius.circular(4.0),
          ),
        ),
        errorBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0x00000000),
            width: 1,
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(4.0),
            topRight: Radius.circular(4.0),
          ),
        ),
        focusedErrorBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0x00000000),
            width: 1,
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(4.0),
            topRight: Radius.circular(4.0),
          ),
        ),
      ),
      style: FlutterFlowTheme.of(context).bodyText1,
    );
  }

Above this function copy these three lines of code. We will use the TextControllers to get the text from the TextField before submitting the form.

final nameController = TextEditingController();
final priceController = TextEditingController();
final quantityController = TextEditingController();

Now let’s create a Stepper Widget. Let’s get the code from Flutter’s official Stepper Widget documentation.
Stepper class – material library – Dart API

Replace your build method with the code from the documentation. At this point, you may want to save the code, then click edit again.

int _index = 0;

@override
  Widget build(BuildContext context) {
    return Stepper(
      currentStep: _index,
      onStepCancel: () {
        if (_index > 0) {
          setState(() {
            _index -= 1;
          });
        }
      },
      onStepContinue: () {
        if (_index <= 0) {
          setState(() {
            _index += 1;
          });
        }
      },
      onStepTapped: (int index) {
        setState(() {
          _index = index;
        });
      },
      steps: <Step>[
        Step(
          title: const Text('Step 1 title'),
          content: Container(
              alignment: Alignment.centerLeft,
              child: const Text('Content for Step 1')),
        ),
        const Step(
          title: Text('Step 2 title'),
          content: Text('Content for Step 2'),
        ),
      ],
    );
  }

We will replace the steps with our custom text fields.

We also copied the index variable, so that we may change Steps.

Replace the steps with the following code. In the first Step widget, we will have only one text field, which is the name field. Notice we are passing the nameController and the hint text.

In the second Step widget we have a column that holds the price text field, quantity text field and the save button. The save button calls a function called submit form, which we haven’t defined yet. We could also wrap the Stepper widget with a column and add the button at the bottom. For simplicity, we will keep it like this.

Step(
   title: const Text('Product Name'),
   content: getTextFormWidget(nameController, 'Enter The Product Name'),
),
Step(
    title: const Text('Product Name'),
    content: Column(
        children: [
            getTextFormWidget(priceController, 'Enter The Product Price'),
            getTextFormWidget(quantityController, 'Enter The Product Quantity'),
            ElevatedButton(
              onPressed: submitForm,
               child: const Text('Save'),
            ),
        ],
     ),
),

Save to Firebase

Above the build function, create another function named submitForm.

 void submitForm() async {

 }

In FlutterFlow’s UI builder, when creating an action to save the data, it has methods it generates that are exposed through backend.dart. It would generate code like this to save data. Although this will work if you download the code, it will not compile on the FlutterFlow platform.

final groceryRequestCreateData = 
    createGroceryItemRecordData( 
        name: nameController.text,
        price: double.parse(priceController.text),
        quantity: int.parse(quantityController.text),
    );
    await GroceryItemRecord.collection
    .doc()
    .set(emergencyRequestCreateData);

So let’s use the FlutterFire documentation to save this data to Firestore.
Cloud Firestore | FlutterFire

The following code shows the solution to submit the code to firebase.

First, we will put the reference to the GroceryItem collection. We are creating a random id using the grocery collection reference.
Can i generate Doc id from flutter to firebase without the method add?

Next, we are using setting the data using the textControllers. We have to parse price and quantity controllers to their respective data types.

CollectionReference groceryItemRef =
      FirebaseFirestore.instance.collection('GroceryItem');

 void submitForm() async {
    try {
      var randomId = groceryItemRef.doc().id;
      await groceryItemRef.doc(randomId).set({
        'name': nameController.text,
        'price': double.parse(priceController.text),
        'quantity': int.parse(quantityController.text)
      });
    } catch (e) {
      print('Error saving grocery item: $e');
    }
  }

Format and Save your code. Click edit again, then click Compile and Preview. You should see a preview of your form.

Test your custom StepperForm widget

Now let’s go back to the UI builder in FlutterFlow. Remove the text field you created and replace it with the custom StepperForm widget.

You can now use Test Mode to test it out.

After you click the Save button, go to Firestore in the Firebase console. You should see the data from your form. You could use a dialog to show the success of the submission.

Here is the complete code:

class StepperForm extends StatefulWidget {
  const StepperForm({
    Key? key,
    this.width,
    this.height,
  }) : super(key: key);

  final double? width;
  final double? height;

  @override
  _StepperFormState createState() => _StepperFormState();
}

class _StepperFormState extends State<StepperForm> {
  final nameController = TextEditingController();
  final priceController = TextEditingController();
  final quantityController = TextEditingController();

  int _index = 0;

  CollectionReference groceryItemRef =
      FirebaseFirestore.instance.collection('GroceryItem');

  void submitForm() async {
    try {
      var randomId = groceryItemRef.doc().id;
      await groceryItemRef.doc(randomId).set({
        'name': nameController.text,
        'price': double.parse(priceController.text),
        'quantity': int.parse(quantityController.text)
      });
    } catch (e) {
      print('Error saving grocery item: $e');
    }
  }

  Widget getTextFormWidget(
      TextEditingController textController, String customHintText) {
    return TextFormField(
      controller: textController,
      autofocus: true,
      obscureText: false,
      decoration: InputDecoration(
        hintText: customHintText,
        hintStyle: FlutterFlowTheme.of(context).bodyText2,
        enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0x00000000),
            width: 1,
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(4.0),
            topRight: Radius.circular(4.0),
          ),
        ),
        focusedBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0x00000000),
            width: 1,
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(4.0),
            topRight: Radius.circular(4.0),
          ),
        ),
        errorBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0x00000000),
            width: 1,
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(4.0),
            topRight: Radius.circular(4.0),
          ),
        ),
        focusedErrorBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0x00000000),
            width: 1,
          ),
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(4.0),
            topRight: Radius.circular(4.0),
          ),
        ),
      ),
      style: FlutterFlowTheme.of(context).bodyText1,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Stepper(
      currentStep: _index,
      onStepCancel: () {
        if (_index > 0) {
          setState(() {
            _index -= 1;
          });
        }
      },
      onStepContinue: () {
        if (_index <= 0) {
          setState(() {
            _index += 1;
          });
        }
      },
      onStepTapped: (int index) {
        setState(() {
          _index = index;
        });
      },
      steps: <Step>[
        Step(
          title: const Text('Product Name'),
          content: getTextFormWidget(nameController, 'Enter The Product Name'),
        ),
        Step(
          title: const Text('Product Name'),
          content: Column(
            children: [
              getTextFormWidget(priceController, 'Enter The Product Price'),
              getTextFormWidget(
                  quantityController, 'Enter The Product Quantity'),
              ElevatedButton(
                onPressed: submitForm,
                child: const Text('Save'),
              ),
            ],
          ),
        ),
      ],
    );
  }
}


Check out my other Flutter Flow articles
FlutterFlow Stepper Form with Firebase Firestore
Flutter Flow: Get Value From Multiple-Choice Choice Chips
Flutter Flow: Carousel Menu
Flutter Flow: Migrating the Carousel Menu to use data types
Flutter Flow: Adding Custom Markers for Google Maps
Flutter Flow: Use Supabase to show custom markers on Google Maps

Resources

Stepper class – material library – Dart API
Cloud Firestore | FlutterFire
TextFormField class – material library – Dart API
Can i generate Doc id from flutter to firebase without the method add?

Flutter Flow: Carousel Menu

Flutter Flow: Carousel Menu

I’m going to discuss how to make a simple carousel menu in the Flutter Flow platform. I will provide the full source code as well.

We are going to use the following package to help us achieve this:
carousel_slider | Flutter Package

Note: Flutter Flow introduced data types. I walk through how to use data types instead of JSON in this new article
Flutter Flow: Migrating the Carousel Menu to use data types

Custom development

I get a lot of requests for custom tweaks of Google Maps with Flutter.

To better understand everyone’s needs, I have created a survey to capture these requests.

https://coffeebytez.substack.com/survey/639443

I will focus on the most highly requested items first, which will be available on my substack. I will also post exclusive in-depth tutorials there.

Please subscribe to my substack here:

https://coffeebytez.substack.com/?r=gdmkm&utm_campaign=pub-share-checklist

If you have urgent specific requests, please leave your contact information in the survey.

Custom Widget

First, let’s create our custom widget. Click the custom functions tab on the left-hand side of the flutter flow platform

Now click Add > Widget

Give your new widget a name such as CarouselMenu

We will need to pass JSON as a parameter to build our menu items. I will discuss this more later. Create a parameter called jsonMenuItems of type JSON.

Click the green View Boilerplate Code button.

It should show this screen.

Click Copy to Editor

Now click Save at the top right of the screen

Adding Carousel Slider dependency

Under the parameters add the carousel_slider dependency.

Click Save again. Then click Compile.

Adding JSON menu items to local state

For this example, we are going to store our menu items in Flutter Flow local state. You could also retrieve the JSON from an API.

Click on the Local State tab.

Now click Add State Variable.

Add a JSON field. You can name it the same as the parameter we created earlier.

Menu Item Example JSON

You can use this example JSON array to populate your menu carousel. This is a JSON Array containing three JSON Objects. Each object is a menu item and contains a title, route, and imageUrl.
{% gist https://gist.github.com/cmcoffeedev/48cf66c91b930840df767d1abbd6968e.js %}
If you look at the Flutter Flow source code you will see they use named routes to navigate in the app using the go router dependency. The route will be each screen’s Scaffold name. You will see this later.

Copy this JSON as the default value for the state variable we just created.

Save this using Ctrl-S/CMD-S.

Writing the custom carousel menu code

Go back to our custom widget so that we may implement the carousel menu.

First, add the import for the carousel_slider and go_router dependencies.

import 'package:go_router/go_router.dart';
import 'package:carousel_slider/carousel_slider.dart';

Create a class called CarouselItem and put it under _CarouselMenuState. This class will hold each menu item from the JSON.

Create a list variable of CarouselItem’s.

Under the carousel item list, let’s override initState. This method get’s called once when the widget first gets created.
initState method – State class – widgets library – Dart API

Under initState, create a method to build our carousel widgets. This method doing 3 things.

  1. Iterating through each JSON Object in the JSON array.
  2. Creating a CarouselItem object with the current object’s value.
  3. Adding this object to the carouselItems list.

Let’s change our build method to return the carousel_slider.

Notice that I have context.pushNamed(route); commented out. This is the code FlutterFlow uses, but in the online editor, it will give you errors when compiling but will work when downloading the project.

There are some workarounds to get it to run on the Flutter Flow platform. Essentially, after you launch the app in test mode, uncomment the code, save it, and click instant reload.

Alternatively, we can instead use the following:

GoRouter.of(context).go(route);

Save and compile what we have so far.

Setting up screens to run our custom widget

Next, go to the Widget Tree tab to create our screens.

Create four screens. One that has our custom widget and three others that the carousel items will navigate to. These are the names that you will use as the route in the custom JSON state.

On the page/screen you want your custom widget, go to the UI Builder and drag and drop the custom widget.

Here are my settings for the custom widget.

Notice I set the JSON variable from our local state JSON variable.

Save the project and click the run in test mode button on the top right.

To test clicking the menu items, go back to the custom code and uncomment it. Save and then compile it. Go back to Test Mode and click instant reload. This will work fine when you download the code.

Here is the full source code:

// Automatic FlutterFlow imports
import '../../backend/backend.dart';
import '../../flutter_flow/flutter_flow_theme.dart';
import '../../flutter_flow/flutter_flow_util.dart';
import '../widgets/index.dart'; // Imports other custom widgets
import '../../flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'package:carousel_slider/carousel_slider.dart';
import 'package:go_router/go_router.dart';

class CarouselMenu extends StatefulWidget {
  const CarouselMenu({
    Key? key,
    this.width,
    this.height,
    this.jsonMenuItems,
  }) : super(key: key);

  final double? width;
  final double? height;
  final dynamic jsonMenuItems;

  @override
  _CarouselMenuState createState() => _CarouselMenuState();
}

class _CarouselMenuState extends State<CarouselMenu> {
  List<CarouselItem> carouselItems = [];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    buildCarouselWidgets();
  }

  void buildCarouselWidgets() {
    for (var data in widget.jsonMenuItems) {
      carouselItems
          .add(CarouselItem(data['imageUrl'], data['title'], data['route']));
    }
  }

  @override
  Widget build(BuildContext context) {
    return CarouselSlider.builder(
      itemCount: carouselItems.length,
      itemBuilder: (context, index, realIndex) {
        var item = carouselItems[index];
        var title = item.title;
        var route = item.route;
        var imageUrl = item.imageUrl;
        return GestureDetector(
          onTap: () {
            GoRouter.of(context).go(route);
          },
          child: Column(
            children: [
              Expanded(
                  child:
                      Image.network(imageUrl, fit: BoxFit.cover, width: 1000)),
              Text(title)
            ],
          ),
        );
      },
      options: CarouselOptions(
        autoPlay: false,
        enlargeCenterPage: true,
        aspectRatio: 2.0,
      ),
    );
  }
}

class CarouselItem {
  String imageUrl = "";
  String title = "";
  String route = "";
  CarouselItem(String imageUrl, String title, String route) {
    this.imageUrl = imageUrl;
    this.title = title;
    this.route = route;
  }
}

Check out my other Flutter Flow articles
FlutterFlow Stepper Form with Firebase Firestore
Flutter Flow: Get Value From Multiple-Choice Choice Chips
Flutter Flow: Carousel Menu
Flutter Flow: Migrating the Carousel Menu to use data types
Flutter Flow: Adding Custom Markers for Google Maps
Flutter Flow: Use Supabase to show custom markers on Google Maps

Migrating away from Firebase, to Netlify and Supabase

Migrating away from Firebase, to Netlify and Supabase

Latest Keith Lee app updates

In my previous article, I showed you how to create a Keith Lee food app using ReactJS, ChatGPT, and Firebase.

However, as time passed, I found that Supabase for the backend and Netlify for hosting are more cost-efficient solutions.

Supabase is also open-source; you don’t have to deal with vendor lock-in as you would with Firebase. This means I can migrate to any hosting provider freely if I want to.

You also don’t have to host and maintain the entire Supabase project. You can connect to any database as a service platform.

Latest Keith Lee app updates

As you can tell from the screenshot, I have added more features, such as authentication, saving places, a new UI, filters, a guided tour, and a new “profile” page for each place.

Due to the complexity of the new features, I have also utilized redux-toolkit. I will write separate articles on my approaches to implementing these new features.

I also have created native iOS and Android apps using SwiftUI and Jetpack Compose. I will go over their implementation in the future as well.

Setting Up Supabase

Supabase is an open-source alternative to Firebase that offers different services, including database, authentication, storage, and real-time subscriptions. To start using Supabase, you need to create an account and a new project.

  1. Head over to https://supabase.com/ and sign up for a free account.
  2. Create a new project and fill in the required details.

Once your project is created, you’ll receive a unique project URL and API key. Save these for later use.

Migrating Data from Firestore to Supabase

Before migrating data from Firestore to Supabase, we need to export data from Firestore.

Supabase has a Firebase migration tool. I’ll list the relevant steps from the documentation so you don’t have to jump between tabs.
Firestore Data Migration

Exporting Firebase data

First, we must clone the firebase-to-supabase repo

git clone https://github.com/supabase-community/firebase-to-supabase.git

In the /firestore directory, create a file named supabase-service.json with the following contents:

{
  "host": "database.server.com",
  "password": "secretpassword",
  "user": "postgres",
  "database": "postgres",
  "port": 5432
}
  1. Go to the Database settings for your project in the Supabase Dashboard.
  2. Under Connection Info, copy the Host string and replace the entry in your supabase-service.json file.
  3. Enter the password you used when you created your Supabase project in the password entry in the supabase-service.json file.

Generate a Firebase private key

  1. Log in to your Firebase Console and open your project.
  2. Click the gear icon next to Project Overview in the sidebar and select Project Settings.
  3. Click Service Accounts and select Firebase Admin SDK.
  4. Click Generate new private key.
  5. Rename the downloaded file to firebase-service.json.

Now we need to export the data to json. Run the following command with your collection name.

node firestore2json.js <collection-name>

In my case, the command will look like the following:

node firestore2json.js places

Import the JSON file into Supabase using the following command

node json2supabase.js <path_to_json_file>

Updating React App to Use Supabase

Now, we need to update our React app to use Supabase instead of Firestore. First, install the Supabase JavaScript client:

npm install @supabase/supabase-js

Also, check out the Supabase Quick Start documentation for React:
Use Supabase with React

The Supabase Javascript docs mention creating the following file to initialize the Supabase client. I’ve slightly changed it to allow using React environment variables.

import {createClient} from "@supabase/supabase-js";

export const supabase = createClient(
    process.env.REACT_APP_SUPABASE_URL,
    process.env.REACT_APP_SUPABASE_KEY,
)
// supabaseClient.js

Now create a file called .env at the root of your project. Each env variable must begin with the prefix REACT_APP_

Add the following to the .env file. Note you can find these under Settings > API.

REACT_APP_SUPABASE_URL=<your-supabase-url>
REACT_APP_SUPABASE_KEY=<your-supabase-key>

You can read more about React env vars in their official documentation:
Adding Custom Environment Variables | Create React App

Now let’s update our code to retrieve the data from Supabase.

 useEffect(() => {
    getPlaces
  }, []);

 const getPlaces = async() =>{
    const { data, error } = await supabase.from("places").select().order('title');
    setData(data);
 }

Migrating from Firebase Hosting to Netlify

Using Netlify is easier to set up than Firebase.

Head over to https://www.netlify.com/ and create a new account. I signed up using Github.

After signing up, Netlify guides you on creating a new project. After creating your project, you can deploy it.

First, before deploying your project, you will need to add a _redirects file to the project directory of your React project.

/*    /index.html   200

Check out the Netlify documentation for more configuration options:
Redirects and rewrites

The easiest way to deploy it depends on your experience.

You can allow Netlify to read your projects from Github, build them, and deploy them.
A Step-by-Step Guide: Deploying on Netlify

You can also create a production build and upload the build folder directly.

npm run build --production

Check out the latest updates to the Keith Lee food app:
Keith Lee Food App

Thank you for taking the time to read this article!

Adding Google Maps to React.js

Adding Google Maps to React.js

In the previous article, I wrote about creating a Keith Lee food app using React.js and Firebase.
Creating a Keith Lee Food App w/ React.js & ChatGPT

In this article, I will show you how I used the Google Maps Javascript SDK to display the list of restaurants on a map.
Google Maps Platform Documentation | Maps JavaScript API | Google Developers

I have discontinued using Firebase and moved to Netlify for hosting and Supabase for the backend. I will create an article on my steps to migrate away from Firebase to Supabase.
Develop and deploy websites and apps in record time | Netlify
The Open Source Firebase Alternative | Supabase

Enabling the Google Maps Javascript SDK

We are going to follow along with the documentation from Google.
Set up your Google Cloud project | Maps JavaScript API | Google Developers

Create a Google Project. Click on the blue Create new project button.

Fill out the form and click Create.

Now we need to enable Google Maps for our project. Go to the following link and click Enable the Maps Javascript API
Set up your Google Cloud project | Maps JavaScript API | Google Developers

The button below will initially state Enable. Click it then it will say **Manage. **Now click Manage

Click Credentials on the left hand side.

You will see your Google Maps API key. We will need this soon. You may also restrict the API key to localhost:3000. When you get a domain, you may add it to the list.

Implementing the Google Maps SDK

We will first need to load the Javascript API in our React project. Go to the following link. We will follow along with this documentation.
Load the Maps JavaScript API | Google Developers

I am going to use the recommended Dynamic Library Import. Add the following Javascript code before the closing body tag in index.html

<script>
  (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
    key: "YOUR_API_KEY_HERE",
    v: "weekly",
    libraries: ["marker", "maps"]
    // Add other bootstrap parameters as needed, using camel case.
    // Use the 'v' parameter to indicate the version to load (alpha, beta, weekly, etc.)
  });
</script>

Notice we are adding the marker and maps libraries. Make sure to add your API key here.

Now let’s create a new React functional component. We can also copy the Firebase code from the previous article.

import {Box} from "@mui/material";


const config = {
  // Your Firebase project's config object goes here
};

firebase.initializeApp(config);
const db = firebase.firestore();

const RestaurantMap = () => {

    return <Box
        sx={{
            height: '100vh',
            width: '100%'
        }}
        id="map"
    >

    </Box>


}

export default RestaurantMap;

You can run the code, and it will show a blank Google map.

We will then load the markers on the google map. Add this right above the return. Note I have comments of numbers, which represent the steps I took to add google map markers. I will detail those steps under the code.

Also, we are querying Firebase again. We could use redux-toolkit to load and save the data only when needed. For now, we will load it again. In the future, I may add redux-toolkit. It would only take a few mins to implement.
Quick Start | Redux Toolkit

useEffect(() => {
    // Get data from the 'tiktoks' collection in Firestore
   const google = window.google; // 1
   const {Map} = await google.maps.importLibrary("maps"); // 2
   const map = new Map(document.getElementById("map"), {  
                    center: {lat: 36.188110, lng: -115.176468},
                    zoom: 12,
               });
   db.collection('tiktoks').get() // 3
      .then(snapshot => {
            const data = snapshot.docs.map(doc => doc.data()); // Each item in firebase

            const position = {lat: place.lat, lng: place.lng};


            const marker = new google.maps.Marker({ // 4
                 map: map,
                 position: position,
                 title: place.name,
            });

            // ...Retrieve variables below depending on what you want to do 

            const information = new google.maps.InfoWindow({ // 5
                content: `
                         <h4>${place.name}</h4>
                         <div>
                             ${address}
                         </div>
                         ${dir}
                         ${callDiv}
                         <hr>
                         <div>
                             <a href="${shareHref}" target="_blank"/>Share</a>
                         </div>
                         <hr>
                          <div>
                             <a href="${tiktokUrl}" target="_blank"/>Watch Review</a>
                         </div>
                         ${orderUrl}
                         `
            });

            marker.addListener('click', function () { // 6
                information.open(map, marker);
            });



      })
      .catch(error => {
        console.log(error);
      });
  }, []);
  1. Add the google library
  2. Import the maps library from google and create Map object. I have this centered in Las Vegas, since this is where most of the food reviews happen.
  3. Get the list of items from Firebase
  4. Add the Google Marker for the current item
  5. Add the InfoWindow to the Google Marker. This is the popup that shows when you click the Google Marker.
  6. Add a click listener to the marker to make the InfoWindow popup when you show it.

You can refer to the documentation for further customization:
Add a Google Map with a Marker to Your Website | Maps JavaScript API | Google Developers

A few days after creating this map with the list of items, I found someone created a Google MyMaps on Reddit.
{% gist %}
I liked it so much I decided to add it to the web app on a separate page.

Thanks for reading. Let me know if any clarification is needed. You can find the Keith Lee food app at the following link:
Keith Lee Food Reviews

Flutter Flow: Migrating the Carousel Menu to use data types

Flutter Flow: Migrating the Carousel Menu to use data types

Previously I walked through the process of adding a Carousel Menu in Flutter using a custom widget
Flutter Flow: Carousel Menu

If you remember, we used a JSON array of objects for each item. Since then, Flutter Flow has added the ability to create custom data types. This approach is also more user-friendly for Flutter Flow beginners.

Custom development

I get a lot of requests for custom tweaks of Google Maps with Flutter.

To better understand everyone’s needs, I have created a survey to capture these requests.

https://coffeebytez.substack.com/survey/639443

I will focus on the most highly requested items first, which will be available on my substack. I will also post exclusive in-depth tutorials there.

Please subscribe to my substack here:

https://coffeebytez.substack.com/?r=gdmkm&utm_campaign=pub-share-checklist

If you have urgent specific requests, please leave your contact information in the survey.

Creating a Carousel Menu Item data type

First, let’s create a data type by clicking on the Data Types menu item on the left.

Next, click the orange plus button to create a new data type. Let’s give it the name CarouselMenuItem

Now let’s add three String fields title, route, and imageUrl

Creating a list of CarouselMenuItem

Now let’s create a list of items to use in our new custom widget.

Click on the App values menu item

Click Add App State Variable

Let’s name the field carouselMenuItems. Next, choose **Data Type **and use our new Carousel Menu Item data type. Make sure to set Is List as well. Click Create

You should now see your new app state.

Let’s add our list of items. I’ll provide the previous JSON, so it’s easier to follow along.
{% gist https://gist.github.com/cmcoffeedev/48cf66c91b930840df767d1abbd6968e.js %}
For each item, you will click Add Item > Tap To Edit Fields

Creating a new custom carousel widget

Click the Custom Code menu item

Click Add > Widget. I gave it the name CarouselMenuEnhanced

Now let’s add the carousel dependency. It’s probably a newer version, but I will stick with the previous version we used for now.

carousel_slider: ^4.2.1

Click Add Dependency, then paste the dependency. Click the green button after pasting the dependency. Click Save.

Now let’s define the new parameters.

Name it carouselMenuItems, choose Data Type as the Type, and check Is List. Choose the Type of Data Type as **CarouselMenuItem. **If you click the green button shown at the top left of the screenshot below, you can get the default code generated. This is optional, I will provide the code as well.

Here is the final code. Let’s walk through the differences

  • First notice the carousel slider package import is the same
  • We have a List of CarouselMenuItemStruct. Previously we had a dynamic type for the json menu items.
  • We don’t need initState or custom class now that we have data types. We just have to change how we reference the items shown
  • We are using GoRouter to navigate. Usually, you wouldn’t do it like this, instead you would use context.pushNamed(route). The Flutter Flow online editor doesn’t compile when using context.pushNamed(route). // Automatic FlutterFlow imports
    import ‘/backend/backend.dart’;
    import ‘/backend/schema/structs/index.dart’;
    import ‘/flutter_flow/flutter_flow_theme.dart’;
    import ‘/flutter_flow/flutter_flow_util.dart’;
    import ‘/custom_code/widgets/index.dart’; // Imports other custom widgets
    import ‘/custom_code/actions/index.dart’; // Imports custom actions
    import ‘/flutter_flow/custom_functions.dart’; // Imports custom functions
    import ‘package:flutter/material.dart’;
    // Begin custom widget code
    // DO NOT REMOVE OR MODIFY THE CODE ABOVE! import ‘package:carousel_slider/carousel_slider.dart’;
    import ‘package:go_router/go_router.dart’; class CarouselMenuEnhanced extends StatefulWidget {
    const CarouselMenuEnhanced({
    Key? key,
    this.width,
    this.height,
    this.carouselMenuItems,
    }) : super(key: key); final double? width;
    final double? height;
    final List? carouselMenuItems; @override
    _CarouselMenuEnhancedState createState() => _CarouselMenuEnhancedState();
    } class _CarouselMenuEnhancedState extends State {
    @override
    Widget build(BuildContext context) {
    int itemCount = widget.carouselMenuItems?.length ?? 0; return CarouselSlider.builder( itemCount: itemCount, itemBuilder: (context, index, realIndex) { var item = widget.carouselMenuItems?[index]; var title = item?.title ?? ""; var route = item?.route ?? "Home Page"; var imageUrl = item?.imageUrl ?? ""; return GestureDetector( onTap: () { GoRouter.of(context).go(route); }, child: Column( children: [ Expanded( child: Image.network(imageUrl, fit: BoxFit.cover, width: MediaQuery.of(context).size.width)), Text(title) ], ), ); }, options: CarouselOptions( autoPlay: false, enlargeCenterPage: true, aspectRatio: 2.0, ), ); }
    }

Add the custom carousel widget to a page

Click on the Widget Pallete menu item, then Components.

Drag the new custom widget to the screen. For the properties, I set width to 100% and height to 300px. I chose our carouselMenuItems that we created in app state for the list.

Save, then run your app in Test Mode.

Congrats on creating a custom carousel menu in Flutter Flow!

One last thing, I have submitted a version of this that is just an image gallery, to the Flutter Flow marketplace. I’m waiting for approval. It has more parameters for further customization. I’m trying to think of an elegant way of handling tapping the item.

I’m thinking about just setting an action as a parameter and letting the user handle it that way. Let me know what you think.

Check out my other Flutter Flow articles
FlutterFlow Stepper Form with Firebase Firestore
Flutter Flow: Get Value From Multiple-Choice Choice Chips
Flutter Flow: Carousel Menu
Flutter Flow: Migrating the Carousel Menu to use data types
Flutter Flow: Adding Custom Markers for Google Maps
Flutter Flow: Use Supabase to show custom markers on Google Maps

Flutter Flow: Use the NHTSA API to get vehicle information

Flutter Flow: Use the NHTSA API to get vehicle information

I’m going to show you how to use the National Highway Traffic Safety Administration API to get the make and model of a car. in the next article, I will demonstrate how to use the VIN of a vehicle to get its information.

Custom development

I get a lot of requests for custom tweaks of Google Maps with Flutter.

To better understand everyone’s needs, I have created a survey to capture these requests.

https://coffeebytez.substack.com/survey/639443

I will focus on the most highly requested items first, which will be available on my substack. I will also post exclusive in-depth tutorials there.

Please subscribe to my substack here:

https://coffeebytez.substack.com/?r=gdmkm&utm_campaign=pub-share-checklist

If you have urgent specific requests, please leave your contact information in the survey.

Adding API Calls

First, on the left side, click the API Calls tab.

Click on the Add button.

Get Car Makes

Now let’s define the car makes API

For the API name I used Get Car Makes

The method type is GET

The API URL is https://vpic.nhtsa.dot.gov/api/vehicles/GetMakesForVehicleType/car?format=json

If you click on the Response & Test tab, click Test API Call, you will see the response. Notice the results have MakeId, MakeName, etc.

Get Car Models

There are two ways to get the make from the model, by using the **MakeId (Get Models for Make) **or MakeName (Get Models for Make) from the make response.

I believe using MakeId would be a better option, because they may change the name anytime. For this simple example, I’m using MakeName.

I gave it the name Get Models For Make

The method type is GET

The API URL is https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMake/[make]?format=json

If you want to use MakeId the URL is https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMakeId/[make]?format=json

Notice I have make in square brackets. In this API we need to pass it as a path parameter.

Go to the Variables tab and add a String parameter

If you click on the Response & Test tab and click Test API Call, you will see the response. Notice the results have Make_Id, Make_Name, Model_Name, etc.

Create data types

For this example, I have created data types for the API responses, by going to the Data Types tab to the left.

Get Vehicle Make and Models UI

Now let’s create the UI for getting a vehicle’s make and models. For this example, I have two dropdown widgets.

I’m going to populate the first one once the screen is loaded. After the user chooses a make from the options, I will make a call to get the models for that vehicle. After the user chooses a model, it will populate the text field below it.

I have created three state variables on the top-level widget.

Makes and models are types of list of Data Types we created earlier

I have a z instead of a s at the end of the models state field. I believe I can’t reuse models because the data type uses it.

The makeAndModel state field is a nullable String

Querying vehicle makes

On the top-level widget, I am also creating an action to make the API Call to get the car makes.

For the Action Output Variable Name, I left it to what Flutter Flow generated.

If the API result was a success I update the page state by setting Results field to the makes page state

Showing make results in a dropdown

To show the list of makes in the first dropdown, click on the dropdown, and find Define Options.

Click the orange settings button, choose the makes page state variable, and choose MakeName as the value to show. In this example, I decided to sort them by the MakeName.

Making the Get Models API call when choosing a vehicle make

For the action of On Selected, on the makes dropdown, let’s create an action.

The first action calls the model API using the make value from the make dropdown. I keep the default generated Action Output Variable Name, but remember it so we can use it in the next steps.

In the success action, I am setting the action output to the modelz page state.

Showing the models in the dropdown

Showing the models in the models dropdown to what we did previously for the makes dropdown.

Use the page state variable modelz, choose the Model_Name as the value to show, and sort the items by Model_Name.

We will also create an action when a model is selected. It will set the String variable to the values of the dropdowns.

Your working example should look similar to this: