Getting Started with React Native

Getting Started with React Native

In this tutorial, you’ll learn how to set up the React Native development environment using both the standard procedure and Expo. You also learn how to create your very first React Native app.

Originally published by Wern Ancheta at https://www.sitepoint.com

With the ever-increasing popularity of smartphones, developers are looking into solutions for building mobile applications. For developers with a web background, frameworks such as Cordova and Ionic, React Native, NativeScript, and Flutter allow us to create mobile apps with languages we’re already familiar with: HTML, XML, CSS, and JavaScript.

In this guide, we’ll take a closer look at React Native. You’ll learn the absolute basics of getting started with it. Specifically, we’ll cover the following:

  • what is React Native
  • what is Expo
  • how to set up an React Native development environment
  • how to create an app with React Native
Prerequisites

This tutorial assumes that you’re coming from a web development background. The minimum requirement for you to be able to confidently follow this tutorial is to know HTML, CSS, and JavaScript. You should also know how to install software on your operating system and work with the command line. We’ll also be using some ES6 syntax, so it would help if you know basic ES6 syntax as well. Knowledge of React is helpful but not required.

What is React Native?

React Native is a framework for building apps that work on both Android and iOS. It allows you to create real native apps using JavaScript and React. This differs from frameworks like Cordova, where you use HTML to build the UI and it will just be displayed within the device’s integrated mobile browser (WebView). React Native has built in components which are compiled to native UI components, while your JavaScript code is executed through a virtual machine. This makes React Native more performant than Cordova.

Another advantage of React Native is its ability to access native device features. There are many plugins which you can use to access native device features, such as the camera and various device sensors. If you’re in need of a platform-specific feature that hasn’t been implemented yet, you can also build your own native modules — although that will require you to have considerable knowledge of the native platform you want to support (Java or Kotlin for Android, and Objective C or Swift for iOS).

If you’re coming here and you’re new to React, you might be wondering what it is. React is a JavaScript library for the Web for building user interfaces. If you’re familiar with MVC, it’s basically the View in MVC. React’s main purpose is to allow developers to build reusable UI components. Examples of these components include buttons, sliders, and cards. React Native took the idea of building reusable UI components and brought it into mobile app development.

What is Expo?

Before coming here, you might have heard of Expo. It’s even recommended in the official React Native docs, so you might be wondering what it is.

In simple terms, Expo allows you to build React Native apps without the initial headache that comes with setting up your development environment. It only requires you to have Node installed on your machine, and the Expo client app on your device or emulator.

But that’s just how Expo is initially sold. In reality, it’s much more than that. Expo is actually a platform that gives you access to tools, libraries and services for building Android and iOS apps faster with React Native. Expo comes with an SDK which includes most of the APIs you can ask for in a mobile app development platform:

Those are just few of the APIs you get access to out of the box if you start building React Native apps with Expo. Of course, these APIs are available to you as well via native modules if you develop your app using the standard React Native setup.

Plain React Native or Expo?

The real question is which one to pick up — React Native or Expo? There’s really no right or wrong answer. It all depends on the context and what your needs are at the moment. But I guess it’s safe to assume that you’re reading this tutorial because you want to quickly get started with React Native. So I’ll go ahead and recommend that you start out with Expo. It’s fast, simple, and easy to set up. You can dive right into tinkering with React Native code and get a feel of what it has to offer in just a couple of hours.

That said, I’ve still included the detailed setup instructions for standard React Native for those who want to do it the standard way. As you begin to grasp the different concepts, and as the need for different native features arises, you’ll actually find that Expo is kind of limiting. Yes, it has a lot of native features available, but not all the native modules that are available to standard React Native projects are supported.

Note: projects like unimodules are beginning to close the gap between standard React Native projects and Expo projects, as it allows developers to create native modules that works for both React Native and ExpoKit.

Setting Up the React Native Development Environment

In this section, we’ll set up the React Native development environment for all three major platforms: Windows, Linux, and macOS. We’ll also cover how to set up the Android and iOS simulators. Lastly, we’ll cover how to set up Expo. If you just want to quickly get started, I recommend that you scroll down to the “Setting up Expo” section.

Here are the general steps for setting up the environment. Be sure to match these general steps to the steps for each platform:

  1. install JDK
  2. install Android Studio or Xcode
  3. install Watchman
  4. update the environment variable
  5. install the emulator
  6. install Node
  7. install React Native CLI

You can skip to the section relevant to your operating system. Some steps — like setting up Android Studio — are basically the same for each operating system, so I’ve put them in their own section:

  • setting up on Windows
  • setting up on Linux
  • setting up on macOS
  • setting up Android Studio
  • install Node
  • setting up Expo
  • setting up emulators
  • install React Native CLI
  • troubleshooting common errors

Setting Up on Windows

This section will show you how to install and configure the software needed to create React Native apps on Windows. Windows 10 was used in testing for this.

Install Chocolatey

Windows doesn’t really come with its own package manager that we can use to install the needed tools. So the first thing we’ll do is install one called Chocolatey. You can install it by executing the following command on the command line or Windows Powershell:

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

We can now install the other tools we need by simply using Chocolatey.

Install Python

Python comes with the command line tools required by React Native:

choco install -y python 2 

Install JDK

The JDK allows your computer to understand and run Java code. Be sure to install JDK version 8 as that’s the one required by React Native:

choco install jdk8 

Install NVM

Node has an installer for Windows. It’s better to use NVM for Windows, as that will enable you to install multiple versions of Node so that you can test new versions, or use a different version depending on the project you’re currently working on. For that, you can use NVM for Windows. Download nvm-setup.zip, extract it and execute nvm-setup.exe to install it.

Install Watchman

Watchman optimizes the compilation time of your React Native app. It’s an optional install if you’re not working on a large project. You can find the install instructions on their website.

Update the Environment Variables

This is the final step in setting up React Native on Windows. This is where we update the environment variables so the operating system is aware of all the tools required by React Native. Follow these steps right before you install the React Native CLI.

  • Go to Control PanelSystem and SecuritySystem. Once there, click the Advanced system settings menu on the left.

  • That will open the system properties window. Click on the Environment Variables button:

  • Under the User variables section, highlight the Path variable and click the edit button.
  • On the edit screen, click the New button and enter the path to the Android SDK and platform tools. For me, it’s on C:\users\myUsername\AppData\Local\Android\Sdk and C:\users\myUsername\AppData\Local\Android\Sdk\platform-tools. Note that this is also where you add the path to the JDK if it isn’t already added:

Setting Up on Linux

This section will show you how to install and configure the tools required for developing React Native apps on Linux. I’ve specifically used Ubuntu 18.04 for testing things out, but you should be able to translate the commands to the Linux distribution you’re using.

Install Prerequisite Tools

The first step is to install the following tools. The first line installs the tools required by Node, and the second line is required by Watchman, which we’ll also install later:

sudo apt-get install build-essential libssl-dev curl 
sudo apt-get install git autoconf automake python-dev 

Install NVM

NVM allows us to install and use multiple versions of Node. You can install it with the following commands:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
source ~/.profile

Note: be sure to check out the latest version from the releases page to ensure the NVM version you’re installing is updated.

Install JDK

As seen earlier, React Native actually compiles the corresponding code to each of the platforms you wish to deploy to. The JDK enables your computer to understand and run Java code. The specific JDK version required by React Native is JDK version 8.

sudo apt-get install openjdk-8-jre 

Install Watchman

Watchman is a tool for watching changes in the file system. It’s mainly used to speed up the compilation process. If you’ve enabled live preview on the app that you’re developing, the changes you make to the app will be reflected faster in the live preview. The following steps require Git to already be installed on your system:

git clone https://github.com/facebook/watchman.git
cd watchman
git checkout v4.9.0
./autogen.sh
./configure
make
sudo make install

You may encounter an issue which looks like the following:

CXX      scm/watchman-Mercurial.o

scm/Mercurial.cpp: In constructor ‘watchman::Mercurial::infoCache::infoCache(std::__cxx11::string)’:

scm/Mercurial.cpp:16:40: error: ‘void* memset(void*, int, size_t)’ clearing an object of non-trivial type ‘struct watchman::FileInformation’; use assignment or value-initialization instead [-Werror=class-memaccess]

   memset(&dirstate, 0, sizeof(dirstate));

                                        ^

In file included from scm/Mercurial.h:10,

                 from scm/Mercurial.cpp:3:

./FileInformation.h:18:8: note: ‘struct watchman::FileInformation’ declared here

 struct FileInformation {

        ^~~~~~~~~~~~~~~

cc1plus: all warnings being treated as errors

make: *** [Makefile:4446: scm/watchman-Mercurial.o] Error 1

Try the following command instead:

./configure --without-python --without-pcre --enable-lenient

Update the Environment Variables

Updating the environment variables is necessary in order for the operating system to be aware of the tools you installed, so you can use them directly from the terminal. Note that this is the final step for setting up all the tools required by React Native. Follow this right before the step for installing the React Native CLI.

To update the environment variables, open your .bash_profile file:

sudo nano ~/.bash_profile

Add the following at the beginning then save the file:

export ANDROID_HOME=$HOME/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools

Note that the path above assumes that the Android SDK is installed on your user’s home directory:

echo $HOME

Setting up on macOS

Having a Mac allows you to develop both Android and iOS apps with React Native. In this section, I’ll show how you can set up the development environment for both Android and iOS.

Installing prerequisite tools

Since macOS already comes with Ruby and cURL by default, the only tool you need to install is Homebrew, a package manager for macOS:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

If you already have it installed, simply update it with the following:

brew update

For iOS, the following are required:

  • Latest version of Xcode: installs the tools required for compiling iOS apps
  • Watchman: for watching file changes
  • NVM: for installing Node

Install JDK

Install JDK version 8 for macOS, as that’s the one required by React Native:

brew tap AdoptOpenJDK/openjdk
brew cask install adoptopenjdk8

Install Watchman

Watchman speeds up the compilation process of your source code. Installing it is required for both Android and iOS:

brew install watchman

Install NVM

NVM allows you to install multiple versions of Node for testing purposes:

brew install nvm
echo "source $(brew — prefix nvm)/nvm.sh" >> .bash_profile

Update Environment Variables

After you’ve installed all the required tools, and right before you install the React Native CLI, it’s time to update the environment variables. This is an important step, because without doing it, the operating system won’t be aware of the tools required by React Native.

To update it, open your .bash_profile file:

sudo nano ~/.bash_profile

Then add the path to the Android SDK and platform tools:

export ANDROID_HOME=/Users/$USER/Library/Android/sdk
export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator

Setting up Android Studio

Android Studio is the easiest way to install the tools required for Android (Android SDK, Android Emulator) so it’s the method I usually recommend for beginners. You can download the installer for your specific platform here.

On Windows and macOS, it has a setup wizard which you can just run, clicking on Next until the install is complete. Be sure to select the .dmg file for macOS or the .exe file for Windows from here if the default download button downloads something different. You can use the following screenshots as a basis for your install.

If you’re on Linux, you need to follow these steps first before you can proceed to installing Android Studio:

  1. Download and extract the .tar.gz file:
tar -xzvf android-studio-ide-183.5522156-linux.tar.gz
  1. Navigate to the extracted directory and go inside the bin directory.
  2. Execute studio.sh. This opens up the installer for Android Studio:
./studio.sh

The Setup Wizard will first greet you with the following screen:

Just click on Next until you see the screen below. The Android SDK, and the latest Android API version, are checked by default. You can also check the Android Virtual Device if you want. This installs the default Android emulator for testing your apps. But I generally recommend using Genymotion instead, as it comes with better tools for testing out different device features:

Once it has installed everything, it will show the following. Just click on Finish to close the Setup Wizard:

The next step is to open Android Studio to configure the SDK platform and tools:

Note: if you’ve previously installed Android Studio, you might have an existing project opened already. In that case, you can launch the SDK Manager from the ToolsSDK Manager top menu. Then check the Show Package Details at the bottom right. This way, you could choose only the sub-components instead of installing the whole thing.

Under the SDK Platforms tab, make sure that the following are checked:

  • Android 9.0 (Pie)
  • Android SDK Platform 28
  • Google APIs Intel x86 Atom_64 System Image or Intel x86 Atom_64 System Image

Under the SDK Tools tab, check the Show Package Details again and make sure that the following are checked:

  • Android SDK Build-Tools 29
  • 28.0.3
  • Android SDK Platform-Tools
  • Android SDK Tools
  • Android Support Repository
  • Google Repository

Check the following if you decided to install the Android Emulator:

  • Intel x86 Emulator Accelerator (HAXM installer)

That will optimize the emulator’s performance.

Install Node

Execute the following commands to install a specific version of Node and set it as the default:

nvm install 11.2.0
nvm alias default 11.2.0

Once installed, you can verify that it works by executing the following:

node --version
npm --version

Here’s what the output will look like:

Setting Up Expo

In this section, we’ll set up Expo, an alternative way of developing React Native apps. This section assumes that you already have Node and Watchman installed.

To set up Expo, all you have to do is install their command line tool via npm:

npm install -g expo-cli

That’s really all there is to it! The next step is to download the Expo client App for Android or iOS. Note that this is the only way you can run Expo apps while you’re still on development. Later on, you can build the standalone version.

From here, you can either proceed to the Hello World App section if you plan on running apps on your device, or the Setting up Emulators section if you want to run it on the Android Emulator or iOS Simulator.

Setting Up Emulators

Emulators allow you to test out apps you’re developing right from your development machine.

Note: emulators require your machine to have at least 4GB of RAM. Otherwise, they’ll really slow down your machine to the point where you get nothing done because of waiting for things to compile or load.

iOS Simulator

Xcode already comes pre-installed with iOS simulators, so all you have to do is launch it before you run your apps. To launch an iOS simulator from the command line, you first have to list the available simulators:

xcrun simctl list

Take note of the device UUID of the device you wish to run and substitute it with the value of UUID of the command below:

 open -a Simulator --args -CurrentDeviceUDID "UUID"

Genymotion

As mentioned earlier, I recommend Genymotion as the emulator for Android, as it has more device features that you can test out — for example, when testing out apps that make use of GPS. Genymotion allows you to select a specific place via a map interface:

To install Genymotion, you first have to download and install VirtualBox. Once that’s done, sign up for a Genymotion account, log in, and download the installer. Windows and macOS come with a corresponding installer. But for Linux, you have to download the installer and make it executable:

chmod +x genymotion-<version><arch>.bin

After that, you can now run the installer:

./genymotion-<version><arch>.bin

Once it’s done installing, you should be able to search for it on your launcher.

Android Emulator

Even though Genymotion is the first thing I recommend, I believe that Android Emulator has its merits as well. For example, it boots up faster and it feels faster in general. I recommend it if your machine has lower specs or if you have no need for Genymotion’s additional features.

When you launch Android Studio, you can select AVD Manager from the configuration options (or ToolsAVD Manager if you currently have an open project).

Click on the Create Virtual Device button on the window that shows up. It will then ask you to choose the device you wish to install:

You can choose any that you want, but ideally, you want to have the ones which already have a Play Store included. It will be especially useful if your app integrates with Google Sign in or other apps, as it will allow you to install those apps with ease.

Next, it will ask you to download the version of Android you wish to install on the device. Simply select the latest version of Android that’s supported by React Native. At the time of writing this tutorial, it’s Android Pie. Note that this is also the same version that we installed for the Android SDK Platform earlier:

Once installed, click Finish to close the current window then click on Next once you see this screen:

Click on Finish on the next screen to create the emulator. It will then be listed on Android Virtual Device Manager. Click on the play button next to the emulator to launch it.

Install React Native CLI

The final step is to install the React Native CLI. This is the command line tool that allows you to bootstrap a new React Native project, link native dependencies, and run the app on a device or emulator:

npm install -g react-native-cli

Once it’s installed, you can try creating a new project and run it on your device or emulator:

react-native init HelloWorldApp
cd HelloWorldApp
react-native run-android
react-native run-ios

Here’s what the app will look like by default:

At this point, you now have a fully functional React Native development environment set up.

Troubleshooting Common Errors

In this section, we’ll look at the most common errors you may encounter when trying to set up your environment.

Could Not Find tools.jar

You may get the following error:

Could not find tools.jar. Please check that /usr/lib/jvm/java-8-openjdk-amd64 contains a valid JDK installation

This means that the system doesn’t recognize your JDK installation. The solution is to re-install it:

sudo apt-get install openjdk-8-jdk

SDK Location Not Found

You may get the following error when you run your app:

FAILURE: Build failed with an exception.

  • What went wrong:

A problem occurred configuring project ':app'.

> SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.

 This means that you haven’t properly added the path to all of the Android tools required by React Native. You can check it by executing the following:

echo $PATH

It should show the following path:

Android/sdk/tools
Android/sdk/tools/bin
Android/sdk/platform-tools

If not, then you have to edit either your .bashrc or .bash_profile file to add the missing path. The config below adds the path to the platform tools:

sudo nano ~/.bash_profile
export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools

Unable to Find Utility “instruments”

If you’re developing for iOS, you might encounter the following error when you try to run the app:

Found Xcode project TestProject.xcodeproj
xcrun: error: unable to find utility "instruments", not a developer tool or in PATH

The problem is that Xcode command line tools aren’t installed yet. You can install them with the following command:

xcode-select --install
“Hello World” App

Now that your development environment is set up, you can start creating the obligatory “hello world” app. The app you’re going to create is a Pokemon search app. It will allow the user to type the name of a Pokemon and view its details.

Here’s what the final output will look like:

You can find the source code on this GitHub repo.

Bootstrapping the App

On your terminal, execute the following command to create a new React Native project:

react-native init RNPokeSearch

For those of you who decided to use Expo instead, here’s the equivalent command for bootstrapping a new React Native project on Expo. Under Managed Workflow, select blank, enter “RNPokeSearch” for the project name, and install dependencies using Yarn:

expo init RNPokeSearch

Just like in the web environment, you can install libraries to easily implement different kinds of functionality in React Native. Once the project is created, we need to install a couple of dependencies: pokemon and axios. The former is used for verifying if the text entered in the search box is a real Pokemon name, while axios is used to make an HTTP request to the API that we’re using: PokeAPI:

yarn add pokemon axios

Note that the above command works on both standard React Native projects and Expo’s managed workflow, since they don’t have any native dependencies. If you’re using Expo’s managed workflow, you won’t be able to use packages that have native dependencies.

React Native Project Directory Structure

Before we proceed to coding, let’s first take a look at the directory structure of a standard React Native project:

Here’s a break down of the most important files and folders that you need to remember:

  • App.js: the main project file. This is where you’ll start developing your app. Any changes you make to this file will be reflected on the screen.
  • index.js: the entry point file of any React Native project. This is responsible for registering the App.js file as the main component.
  • package.json: where the name and versions of the libraries you installed for this project are added.
  • node_modules: where the libraries you installed are stored. Note that this already contains a lot of folders before you installed the two libraries earlier. This is because React Native also has its own dependencies. The same is true for all the other libraries you install.
  • src: acts as the main folder which stores all the source code related to the app itself. Note that this is only a convention. The name of this folder can be anything. Some people use app as well.
  • android: where the Android-related code is. React Native isn’t a native language. That’s why we need this folder to bootstrap the Android app.
  • ios: where the iOS-related code is. This accomplishes the same thing as the android folder, but for iOS.

Don’t mind the rest of the folders and files for now, as we won’t be needing them when just getting started.

For Expo users, your project directory will look like this:

As you can see, it’s pretty much the same. The only difference is that there’s no android and ios folders. This is because Expo takes care of running the app for you on both platforms. There’s also the addition of the assets folder. This is where app assets such as icons and splash screens are stored.

Running the App

At this point, you can now run the app. Be sure to connect your device to your computer, or open your Android emulator or iOS simulator before doing so:

react-native run-android
react-native run-ios

You already saw what the default screen looks like earlier.

If you’re on Expo, you can run the project with the following command. Be sure you’ve already installed the corresponding Expo client for your phone’s operating system before doing so:

yarn start

Once it’s running, it will display the QR code:

Open your Expo client app, and in the projects tab click on Scan QR Code. This will open the app on your Android or iOS device. If you have an emulator running, you can either press i to run it on the iOS simulator or a to run it on the Android emulator:

If you’re testing on a real device, shake it so the developer menu will show up:

Click on the following:

  • Enable Live Reload: automatically reloads your app when you hit save on any of its source code.
  • Start Remote JS Debugging: for debugging JavaScript errors on the browser. You can also use react-native log-android or react-native log-ios for this, but remote debugging has a nicer output, so it’s easier to inspect.

If you want, you can also set the debug server. This allows you to disconnect your mobile device from your computer while you develop the app. You can do that by selecting Dev Settings in the developer menu. Then under the Debugging section, select Debug server host & port for device. This opens up a prompt where you can enter your computer’s internal IP address and the port where Metro Bundler runs on (usually port 8081).

Once you’ve set that, select Reload from the developer menu to commit the changes. At this point, you can now disconnect your device from the computer. Note that any time you install a package, you have to connect your device, quit Metro Bundler, and run the app again (using react-native run-android or react-native run-ios). That’s the only way for the changes to take effect.

Coding the App

Both standard React Native projects and Expo have built-in components which you can use to accomplish what you want. Simply dig through the documentation and you’ll find information on how to implement what you need. In most cases, you either need a specific UI component or an SDK which works with a service you plan on using. You can use Native Directory to look for those or just plain old Google. More often than not, here’s what your workflow is going to look like:

  1. Look for an existing package which implements what you want.
  2. Install it.
  3. Link it — only for native modules. If you’re on Expo, you don’t really need to do this because you can only install pure JavaScript libraries — although this might change soon because of the introduction of unimodules and bare workflow.
  4. Use it on your project.

Now that you’ve set up your environment and learned a bit about the workflow, we’re ready to start coding the app.

Start by replacing the contents of the App.js file with the following code:

import React from 'react';

import Main from './src/Main';

 

function App() {

 

  return <Main />

 

}

 

export default App;

The first line in the code above code imports React. You need to import this class any time you want create a component.

The second line is where we import a custom component called Main. We’ll create it later. For now, know that this is where we’ll put the majority of our code.

After that, we create the component by creating a new function. All this function does is return the Main component.

Lastly, we export the class so that it can be imported somewhere else. In this case, it’s actually imported from the index.js file.

Next, create the src/Main.js file and add the following:

// src/Main.js
import React, { Component } from 'react';
import { SafeAreaView, View, Text, TextInput, Button, Alert, StyleSheet, ActivityIndicator } from 'react-native';

The second line imports the components that are built into React Native. Here’s what each one does:

  • SafeAreaView: for rendering content within the safe area boundaries of a device. This automatically adds a padding that wraps its content so that it won’t be rendered on camera notches and sensor housing area of a device.
  • View: a fundamental UI building block. This is mainly used as a wrapper for all the other components so they’re structured in such a way that you can style them with ease. Think of it as the equivalent of <div>: if you want to use Flexbox, you have to use this component.
  • Text: for displaying text.
  • TextInput: the UI component for inputting text. This text can be plain text, email, password, or a number pad.
  • Button: for showing a platform-specific button. This component looks different based on the platform it runs on. If it’s Android, it uses Material Design. If it’s iOS, it uses Cupertino.
  • Alert: for showing alerts and prompts.
  • ActivityIndicator: for showing a loading animation indicator.
  • StyleSheet: for defining the component styles.

Next, import the libraries we installed earlier:

import axios from 'axios';
import pokemon from 'pokemon';

We’ll also be creating a custom Pokemon component later. This one is used for displaying Pokemon data:

import Pokemon from './components/Pokemon';

Because getting the required Pokemon data involves making two API requests, we have to set the API’s base URL as a constant:

const POKE_API_BASE_URL = "https://pokeapi.co/api/v2";

Next, define the component class and initialize its state:

export default class Main extends Component {

 

  state = {

    isLoading: false, // decides whether to show the activity indicator or not

    searchInput: '', // the currently input text

    name: '', // Pokemon name

    pic: '', // Pokemon image URL

    types: [], // Pokemon types array

    desc: '' // Pokemon description

  }

 

  // next: add render() method

}

 In the code above, we’re defining the main component of the app. You can do this by defining an ES6 class and having it extend React’s Component class. This is another way of defining a component in React. In the App.js file, we created a functional component. This time we’re creating a class-based component.

The main difference between the two is that functional components are used for presentation purposes only. Functional components have no need to keep their own state because all the data they require is just passed to them via props. On the other hand, class-based components maintain their own state and they’re usually the ones passing data to functional components.

If you want to learn more about the difference between functional and class-based components, read this tutorial: Functional vs Class-Components in React.

Going back to the code, we’re initializing the state inside our component. You define it as a plain JavaScript object. Any data that goes into the state should be responsible for changing what’s rendered by the component. In this case, we put in isLoading to control the visibility of the activity indicator and searchInput to keep track of the input value in the search box.

This is an important concept to remember. React Native’s built-in components, and even the custom components you create, accept properties that control the following:

  • what’s displayed on the screen (data source)
  • how they present it (structure)
  • what it looks like (styles)
  • what actions to perform when user interacts with it (functions)

We’ll go through those properties in more detail in the next section. For now, know that the value of those properties are usually updated through the state.

The rest of the state values are for the Pokemon data. It’s a good practice to set the initial value with the same type of data you’re expecting to store later on — as this serves as documentation as well.

Anything that’s not used for rendering or data flow can be defined as an instance variable, like so:

class Main extends Component {

 

    this.appTitle = "RNPokeSearch";

 

}

Alternatively, it can be defined as an outside-the-class variable, just like what we did with the POKEAPIBASE_URL earlier:

const POKE_API_BASE_URL = '';

 

class Main extends Component {

 

}

Structuring and Styling Components

Let’s return to the component class definition. When you extend React’s Component class, you have to define a render() method. This contains the code for returning the component’s UI and it’s made up of the React Native components we imported earlier.

Each component has its own set of props. These are basically attributes that you pass to the component to control a specific aspect of it. In the code below, most of them have the style prop, which is used to modify the styles of a component. You can pass any data type as a prop. For example, the onChangeText prop of the TextInput is a function, while the types prop in the Pokemon is an array of objects. Later on in the Pokemon component, you’ll see how the props will be used:

render() {

  const { name, pic, types, desc, searchInput, isLoading } = this.state; // extract the Pokemon data from the state

  return (

    <SafeAreaView style={styles.wrapper}>

      <View style={styles.container}>

        <View style={styles.headContainer}>

          <View style={styles.textInputContainer}>

            <TextInput

              style={styles.textInput}

              onChangeText={(searchInput) => this.setState({searchInput})}

              value={this.state.searchInput}

              placeholder={"Search Pokemon"}

            />

          </View>

          <View style={styles.buttonContainer}>

            <Button

              onPress={this.searchPokemon}

              title="Search"

              color="#0064e1"

            />

          </View>

        </View>

 

        <View style={styles.mainContainer}>

          {

            isLoading &&

            <ActivityIndicator size="large" color="#0064e1" />

          }

 

          {

            !isLoading &&

            <Pokemon

              name={name}

              pic={pic}

              types={types}

              desc={desc} />

          }

        </View>

      </View>

    </SafeAreaView>

  );

}

 Breaking down the code above, we first extract the state data:

const { name, pic, types, desc, searchInput, isLoading } = this.state;

Next, we return the component’s UI, which follows this structure:

SafeAreaView.wrapper

  View.container

    View.headContainer

      View.textInputContainer

        TextInput

      View.buttonContainer

        Button

    View.mainContainer

      ActivityIndicator

      Pokemon

The above structure is optimized for using Flexbox. Go ahead and define the component styles at the bottom of the file:

const styles = StyleSheet.create({

  wrapper: {

    flex: 1

  },

  container: {

    flex: 1,

    padding: 20,

    backgroundColor: '#F5FCFF',

  },

  headContainer: {

    flex: 1,

    flexDirection: 'row',

    marginTop: 100

  },

  textInputContainer: {

    flex: 2

  },

  buttonContainer: {

    flex: 1

  },

  mainContainer: {

    flex: 9

  },

  textInput: {

    height: 35,

    marginBottom: 10,

    borderColor: "#ccc",

    borderWidth: 1,

    backgroundColor: "#eaeaea",

    padding: 5

  }

});

In React Native, you define styles by using StyleSheet.create() and passing in the object that contains your styles. These style definitions are basically JavaScript objects, and they follow the same structure as your usual CSS styles:

element: {

  property: value

}

The wrapper and container is set to flex: 1, which means it will occupy the entirety of the available space because they have no siblings. React Native defaults to flexDirection: 'column', which means it will lay out the flex items vertically, like so:

In contrast, (flexDirection: 'row') lays out items horizontally:

It’s different for headContainer, because even though it’s set to flex: 1, it has mainContainer as its sibling. This means that headContainer and mainContainer will both share the same space. mainContainer is set to flex: 9 so it will occupy the majority of the available space (around 90%), while headContainer will only occupy about 10%.

Let’s move on to the contents of headContainer. It has textInputContainer and buttonContainer as its children. It’s set to flexDirection: 'row', so that its children will be laid out horizontally. The same principle applies when it comes to space sharing: textInputContainer occupies thw thirds of the available horizontal space, while buttonContainer only occupies one third.

The rest of the styles are pretty self explanatory when you have a CSS background. Just remember to omit - and set the following character to uppercase. For example, if you want to set background-color, the React Native equivalent is backgroundColor.

Note: not all CSS properties that are available on the Web are supported on React Native. For example, things like floats or table properties aren’t supported. You can find the list of supported CSS properties in the docs for View and Text components. Someone has also compiled a React Native Styling Cheat Sheet. There’s also a style section in the documentation for a specific React Native component that you want to use. For example, here are the style properties that you can use for the Image component.

Event Handling and Updating the State

Let’s now break down the code for the TextInput and Button components. In this section, we’ll talk about event handling, making HTTP requests, and updating the state in React Native.

Let’s start by examining the code for TextInput:

<TextInput

  style={styles.textInput}

  onChangeText={(searchInput) => this.setState({searchInput})}

  value={searchInput}

  placeholder={"Search Pokemon"}

/>

In the above code, we’re setting the function to execute when the user inputs something in the component. Handling events like these are similar to how it’s handled in the DOM: you simply pass the event name as a prop and set its value to the function you wish to execute. In this case, we’re simply inlining it because we’re just updating the state. The value input by the user is automatically passed as an argument to the function you supply so all you have to do is update the state with that value. Don’t forget to set the value of the TextInput to that of the state variable. Otherwise, the value input by the user won’t show as they type on it.

Next, we move on to the Button component. Here, we’re listening for the onPress event:

<Button

  onPress={this.searchPokemon}

  title="Search"

  color="#0064e1"

/>

Once pressed, the searchPokemon() function is executed. Add this function right below the render() method. This function uses the async/await pattern because performing an HTTP request is an asynchronous operation. You can also use Promises, but to keep our code concise, we’ll stick with async/await instead. If you’re not familiar with it, be sure to read this tutorial:

render() {

  // ...

}

 

searchPokemon = async () => {

  try {

    const pokemonID = pokemon.getId(this.state.searchInput); // check if the entered Pokemon name is valid

 

    this.setState({

      isLoading: true // show the loader while request is being performed

    });

 

    const { data: pokemonData } = await axios.get(${POKE_API_BASE_URL}/pokemon/${pokemonID});

    const { data: pokemonSpecieData } = await axios.get(${POKE_API_BASE_URL}/pokemon-species/${pokemonID});

 

    const { name, sprites, types } = pokemonData;

    const { flavor_text_entries } = pokemonSpecieData;

 

    this.setState({

      name,

      pic: sprites.front_default,

      types: this.getTypes(types),

      desc: this.getDescription(flavor_text_entries),

      isLoading: false // hide loader

    });

 

  } catch (err) {

    Alert.alert("Error", "Pokemon not found");

  }

}

 Breaking down the code above, we first check if the entered Pokemon name is valid. If it’s valid, the National Pokedex ID (if you open the link, that’s the number on top of the Pokemon name) is returned and we supply it as a parameter for the HTTP request. The request is made using axios’ get() method, which corresponds to an HTTP GET request. Once the data is available, we extract what we need and update the state.

Here’s the getTypes() function. All it does is reassign the slot and type properties of the Pokemon types to id and name:

getTypes = (types) => {

  return types.map(({ slot, type }) => {

    return {

      "id": slot,

      "name": type.name

    }

  });

}

Here’s the getDescription() function. This finds the first English version of the flavor_text:

getDescription = (entries) => {

  return entries.find(item => item.language.name === 'en').flavor_text;

}

Pokemon Component

Earlier, we imported and used a component called Pokemon, but we haven’t really created it yet. Let’s go ahead and do so. Create a src/components/Pokemon.js file and add the following:

// src/components/Pokemon.js

import React from 'react';

import { View, Text, Image, FlatList, StyleSheet } from 'react-native';

 

const Pokemon = ({ name, pic, types, desc }) => {

  if (!name) {

    return null

  }

 

  return (

    <View style={styles.mainDetails}>

      <Image

        source={{uri: pic}}

        style={styles.image} resizeMode={"contain"} />

        <Text style={styles.mainText}>{name}</Text>

 

        <FlatList

          columnWrapperStyle={styles.types}

          data={types}

          numColumns={2}

          keyExtractor={(item) => item.id.toString()}

          renderItem={({item}) => {

            return (

              <View style={[styles[item.name], styles.type]}>

                <Text style={styles.typeText}>{item.name}</Text>

              </View>

            )

          }}

        />

 

        <View style={styles.description}>

          <Text>{desc}</Text>

        </View>

    </View>

  );

}

 

//

const styles = StyleSheet.create({

  mainDetails: {

    padding: 30,

    alignItems: 'center'

  },

  image: {

    width: 100,

    height: 100

  },

  mainText: {

    fontSize: 25,

    fontWeight: 'bold',

    textAlign: 'center'

  },

  description: {

    marginTop: 20

  },

  types: {

    flexDirection: 'row',

    marginTop: 20

  },

  type: {

    padding: 5,

    width: 100,

    alignItems: 'center'

  },

  typeText: {

    color: '#fff',

  },

  normal: {

    backgroundColor: '#8a8a59'

  },

  fire: {

    backgroundColor: '#f08030'

  },

  water: {

    backgroundColor: '#6890f0'

  },

  electric: {

    backgroundColor: '#f8d030'

  },

  grass: {

    backgroundColor: '#78c850'

  },

  ice: {

    backgroundColor: '#98d8d8'

  },

  fighting: {

    backgroundColor: '#c03028'

  },

  poison: {

    backgroundColor: '#a040a0'

  },

  ground: {

    backgroundColor: '#e0c068'

  },

  flying: {

    backgroundColor: '#a890f0'

  },

  psychic: {

    backgroundColor: '#f85888'

  },

  bug: {

    backgroundColor: '#a8b820'

  },

  rock: {

    backgroundColor: '#b8a038'

  },

  ghost: {

    backgroundColor: '#705898'

  },

  dragon: {

    backgroundColor: '#7038f8'

  },

  dark: {

    backgroundColor: '#705848'

  },

  steel: {

    backgroundColor: '#b8b8d0'

  },

  fairy: {

    backgroundColor: '#e898e8'

  }

});

 

export default Pokemon;

 In the code above, we first checked if the name has a falsy value. If it has, we simply return null as there’s nothing to render.

We’re also using two new, built-in React Native components:

  • Image: used for displaying images from the Internet or from the file system
  • FlatList: used for displaying lists

As we saw earlier, we’re passing in the Pokemon data as prop for this component. We can extract those props the same way we extract individual properties from an object:

const Pokemon = ({ name, pic, types, desc }) => {

  // .. 

}

The Image component requires the source to be passed to it. The source can either be an image from the file system, or, in this case, an image from the Internet. The former requires the image to be included using require(), while the latter requires the image URL to be used as the value of the uri property of the object you pass to it.

resizeMode allows you to control how the image will be resized based on its container. We used contain, which means it will resize the image so that it fits within its container while still maintaining its aspect ratio. Note that the container is the Image component itself. We’ve set its width and height to 100, so the image will be resized to those dimensions. If the original image has a wider width than its height, a width of 100 will be used, while the height will adjust accordingly to maintain the aspect ratio. If the original image dimension is smaller, it will simply maintain its original size:

<Image

  source={{uri: pic}}

  style={styles.image}

  resizeMode={"contain"} />

 Next is the FlatList component. It’s used for rendering a list of items. In this case, we’re using it to render the types of the Pokemon. This requires the data, which is an array containing the items you want to render, and the renderItem, which is the function responsible for rendering each item on the list. The item in the current iteration can be accessed the same way props are accessed in a functional component:

<FlatList

  columnWrapperStyle={styles.types}

  data={types}

  numColumns={2}

  keyExtractor={(item) => item.id.toString()}

  renderItem={({item}) => {

    return (

      <View style={[styles[item.name], styles.type]}>

        <Text style={styles.typeText}>{item.name}</Text>

      </View>

    )

  }}

/>

 In the code above, we also supplied the following props:

  • columnWrapperStyle: used for specifying the styles for each column. In this case, we want to render each list item inline, so we’ve specified flexDirection: 'row'.
  • numColumns: the maximum number of columns you want to render for each row on the list. In this case, we’ve specified 2, because a Pokemon can only have two types at most.
  • keyExtractor: the function to use for extracting the keys for each item. You can actually omit this one if you pass a key prop to the outer-most component of each of the list items.

At this point, you can now test the app on your device or emulator:

react-native run-android
react-native run-ios
yarn start
Conclusion

That’s it! In this tutorial, you’ve learned how to set up the React Native development environment using both the standard procedure and Expo. You also learned how to create your very first React Native app.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading about React Native

The Complete React Native and Redux Course

React Native - The Practical Guide

The complete React Native course ( 2nd edition )

Build CRUD Mobile App using React Native, Elements, Navigation, Apollo Client and GraphQL

Which one is best for you? Flutter, React Native, Ionic or NativeScript?

Accepting payments in React Native

Microsoft launches React Native for Windows

Adding Authentication to React Native Chat App Using Auth0


Flutter - State Management using PROVIDER

Flutter - State Management using PROVIDER

In this tutorial you will see the very basics of implementing "Provider" for State management in your Flutter Applications.

In this tutorial you will see the very basics of implementing "Provider" for State management in your Flutter Applications.

So Let’s get started

Before looking into providers lets see whatsis ChangeNotifier this plugin uses ChangeNotifier to to listen and update any changes.

What is ChangeNotifier

Form docs

A class that can be extended or mixed in that provides a change notification API using [VoidCallback] for notifications.> [ChangeNotifier] is optimized for small numbers (one or two) of listeners. It is O(N) for adding and removing listeners and O(N²) for dispatching notifications (where N is the number of listeners)##

Provider

Existing providers

provider exposes a few different kinds of "provider" for different types of objects.

Let's get started with our code

first things first let add plugin to pubspec.yaml

provider: ^2.0.1
http: ^0.12.0+2

Let's Write our provider class first we name it AppState

import 'package:flutter/material.dart';

class AppState with ChangeNotifier {
  AppState();

  String _displayText = "";

  void setDisplayText(String text) {
    _displayText = text;
    notifyListeners();
  }

  String get getDisplayText => _displayText;
}

Our AppState is extended with ChangeNotifier which is used to notify its listeners when we call notifyListeners()

In the code, we declared two methods setDisplayText and getDisplayText which are used to read and write the value in our state

Now we move to our main.dart

import 'package:flutter/material.dart';
import 'package:flutter_demo_provider/app_state.dart';
import 'package:flutter_demo_provider/text_display.dart';
import 'package:flutter_demo_provider/text_edit.dart';
import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: ChangeNotifierProvider<AppState>(
          builder: (_) => AppState(),
          child: MyHomePage(),
        ));
  }
}

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

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        padding: const EdgeInsets.all(16.0),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              TextDisplay(),
              TextEditWidget(),
            ],
          ),
        ),
      ),
    );
  }
}

In the code, we can notice we have used ChangeNotifierProvider which is provided from out provider plugin

It accepts two parameters one is builder and the other is child

return MaterialApp(
 title: 'Flutter Demo',
 theme: ThemeData(
 primarySwatch: Colors.blue,
 ),
 home: ChangeNotifierProvider<AppState>(
 builder: (_) => AppState(),
 child: MyHomePage(),
 ));
}

Inside the MyHomePage we have a Scaffold with Column which has two Widgets TextDisplay() and TextEditWidget()

TextDisplay(),
TextEditWidget(),

Here is out TextDisplay() in text_display.dart

import 'package:flutter/material.dart';
import 'package:flutter_demo_provider/app_state.dart';
import 'package:provider/provider.dart';

class TextDisplay extends StatefulWidget {
  @override
  _TextDisplayState createState() => _TextDisplayState();
}

class _TextDisplayState extends State<TextDisplay> {
  @override
  Widget build(BuildContext context) {
    final appState = Provider.of<AppState>(context);

    return Container(
      padding: const EdgeInsets.all(16.0),
      child: Text(
        appState.getDisplayText,
        style: TextStyle(
          fontSize: 24.0,
        ),
      ),
    );
  }
}

In the above code we see

Widget build(BuildContext context) {
 final appState = Provider.of<AppState>(context);

 return Container(
 padding: const EdgeInsets.all(16.0),
 child: Text(
 appState.getDisplayText,
 style: TextStyle(
 fontSize: 24.0,
 ),
 ),
 );
}
final appState = Provider.of<AppState>(context);

This above line of code will get the provider for listening for any changes optionally we can also opt-out for listening by proving listen: false

final appState = Provider.of<AppState>(context, listen: false);

Now in order to access text, we have a function in our provider called getDisplayText

appState.getDisplayText()

Here is out TextEditWidget() in text_edit.dart

  
import 'package:flutter/material.dart';
import 'package:flutter_demo_provider/app_state.dart';
import 'package:provider/provider.dart';

class TextEditWidget extends StatefulWidget {
  @override
  _TextEditWidgetState createState() => _TextEditWidgetState();
}

class _TextEditWidgetState extends State<TextEditWidget> {
  TextEditingController _textEditingController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final appState = Provider.of<AppState>(context);

    return Container(
      child: TextField(
        controller: _textEditingController,
        decoration: InputDecoration(
          labelText: "Some Text",
          border: OutlineInputBorder(),
        ),
        onChanged: (changed) => appState.setDisplayText(changed),
        onSubmitted: (submitted) => appState.setDisplayText(submitted),
      ),
    );
  }
}

In the above code, we get our appState inside the build function

final appState = Provider.of<AppState>(context);

In order to manipulate the text in the state we call setDisplayText(text) function

TextField(
 controller: _textEditingController,
 decoration: InputDecoration(
 labelText: "Some Text",
 border: OutlineInputBorder(),
 ),
 onChanged: (changed) => appState.setDisplayText(changed),
 onSubmitted: (submitted) => appState.setDisplayText(submitted),
)

we are updating the state whenever out text is changes

onChanged: (changed) => appState.setDisplayText(changed)

Now we perform network operation

Now inside our app state, we have some additional functions and variables

String _dataUrl = "https://reqres.in/api/users?per_page=20";
String _jsonResonse = "";
bool _isFetching = false;

bool get isFetching => _isFetching;

Future<void> fetchData() async {
 _isFetching = true;
 notifyListeners();

 var response = await http.get(_dataUrl);
 if (response.statusCode == 200) {
 _jsonResonse = response.body;
 }

 _isFetching = false;
 notifyListeners();
}

String get getResponseText => _jsonResonse;

List<dynamic> getResponseJson() {
 if (_jsonResonse.isNotEmpty) {
 Map<String, dynamic> json = jsonDecode(_jsonResonse);
 return json['data'];
 }
 return null;
}

Here we have a few more functions fetchData, getResponseText and getResponseJson

fetchData will perform the network operation and update the variable with the response data (you can parse your JSON to custom model here and save it in a List)

getResponseText will return the plain text response

getResponseJson will convert the response text to a Map and returndata field inside it which is a list of Map

To see the final app_state.dart visit below link

Now inside out MyHomePage widget we add two more widgets to our column

RaisedButton(
 onPressed: () => appState.fetchData(),
 child: Text("Fetch Data from Network"),
),
ResponseDisplay(),

So I am calling appState.fetchData() whenever I press the button now fetchData will take care of all the updating of the state on Github

Here is our ResponseDisplay Widget named response_display.dart

  
import 'package:flutter/material.dart';
import 'package:flutter_demo_provider/app_state.dart';
import 'package:provider/provider.dart';

class ResponseDisplay extends StatefulWidget {
  @override
  _ResponseDisplayState createState() => _ResponseDisplayState();
}

class _ResponseDisplayState extends State<ResponseDisplay> {
  @override
  Widget build(BuildContext context) {
    final appState = Provider.of<AppState>(context);

    return Container(
      padding: const EdgeInsets.all(16.0),
      child: appState.isFetching
          ? CircularProgressIndicator()
          : appState.getResponseJson() != null
              ? ListView.builder(
                  primary: false,
                  shrinkWrap: true,
                  itemCount: appState.getResponseJson().length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      leading: CircleAvatar(
                        backgroundImage: NetworkImage(
                            appState.getResponseJson()[index]['avatar']),
                      ),
                      title: Text(
                        appState.getResponseJson()[index]["first_name"],
                      ),
                    );
                  },
                )
              : Text("Press Button above to fetch data"),
    );
  }
}

Here in the above code we parse the JSON and build a list of data

To get the code it's here on GitHub

Flutter - GPS Geolocation Tutorial

Flutter - GPS Geolocation Tutorial

This tutorial shows you how to access device location in Flutter using GPS, including how to get permissions, get current location and continuous location update.

This tutorial shows you how to access device location in Flutter using GPS, including how to get permissions, get current location and continuous location update.

GPS has become a standard feature on modern smartphones. It's usually used by applications to get the device location.

Dependencies

A Flutter package geolocator provides geolocation functionalities. Add it in your pubspec.yaml file and run Get Packages.

  dependencies {
    ...
    geolocator: ^3.0.1
    ...
  }

Permissions

You need to add permissions to each platform. For Android, you need ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION. Add the following inAndroidManifest.xml.

  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

For iOS, you need NSLocationWhenInUseUsageDescription permission. Add it in the Info.plist file.

  <key>NSLocationWhenInUseUsageDescription</key>
  <string>This app needs access to location when open.</string>

Code Example

Below is the code structure for this tutorial. We need to create an instance of Geolocator and store the value of latest Position. The application will use the Position value to display the latitude and the longitude.

  import 'dart:async';
  import 'package:flutter/material.dart';
  import 'package:geolocator/geolocator.dart';

  class GeolocationExampleState extends State<GeolocationExample> {
    Geolocator _geolocator;
    Position _position;

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

      _geolocator = Geolocator();
    }

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Flutter Geolocation Example'),
        ),
        body: Center(
            child: Text(
               'Latitude: ${_position != null ? _position.latitude.toString() : '0'},'
                  ' Longitude: ${_position != null ? _position.longitude.toString() : '0'}'
            )
        ),
      );
    }
  }

Check Permission

If you’ve added the right permissions, the application will be granted with permissions to access the device location using GPS. In Android 6.0 and above, it will ask the user to grant the permission. But if you need to check whether the application has permission to access location, you can do it programatically. To do so, use checkGeolocationPermissionStatus method which returns Future. Optionally, for iOS, you can check locationAlways and locationWhenInUse separately by passing locationPermission optional parameter whose type is GeolocationPermission

  void checkPermission() {
    _geolocator.checkGeolocationPermissionStatus().then((status) { print('status: $status'); });
    _geolocator.checkGeolocationPermissionStatus(locationPermission: GeolocationPermission.locationAlways).then((status) { print('always status: $status'); });
    _geolocator.checkGeolocationPermissionStatus(locationPermission: GeolocationPermission.locationWhenInUse)..then((status) { print('whenInUse status: $status'); });
  }

Get Current Location

Getting the current location is very simple. Just use getCurrentPosition method which returns Future<Location>. You can pass desiredAccuracy option.

  await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high)

Sometimes the process of getting current location may fail, for example if the user turns off the GPS sensor. If it has been turned of since the beginning, it may cause error, so we need to catch the error. There’s also possible the GPS is turned off while the process of finding location is on going. On this case, it may cause the process stuck, and therefore it’s better to add execution timeout.

  void updateLocation() async {
    try {
      Position newPosition = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
          .timeout(new Duration(seconds: 5));

      setState(() {
        _position = newPosition;
      });
    } catch (e) {
      print('Error: ${e.toString()}');
    }
  }

Below are the descriptions of each LocationAccuracy value.

Name Description lowest Location is accurate within a distance of 3000m on iOS and 500m on Android. low Location is accurate within a distance of 1000m on iOS and 500m on Android. medium Location is accurate within a distance of 10m on iOS and between 0m and 100m on Android. high Location is accurate within a distance of 10m on iOS and between 0m and 100m on Android. best Location is accurate within a distance of ~0m on iOS and between 0m and 100m on Android. bestForNavigation Location is accuracy is optimized for navigation on iOS and matches LocationAccuracy.best on Android. Location Update Stream

To get the updated location, actually you can put the code above in a while loop. But, there’s a better and cleaner way. You can use getPositionStream which returns Stream<Subscription>. You can also set how much location change is needed before the listener is notified using distanceFilter.

  LocationOptions locationOptions = LocationOptions(accuracy: LocationAccuracy.high, distanceFilter: 1);

  StreamSubscription positionStream = _geolocator.getPositionStream(locationOptions).listen(
            (Position position) {
          _position = position;
        });

Below is the full code of this tutorial

  import 'dart:async';
  import 'package:flutter/material.dart';
  import 'package:geolocator/geolocator.dart';

  void main() => runApp(MyApp());

  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Flutter Geolocation',
        home: GeolocationExample(),
      );
    }
  }

  class GeolocationExampleState extends State {
    Geolocator _geolocator;
    Position _position;

    void checkPermission() {
      _geolocator.checkGeolocationPermissionStatus().then((status) { print('status: $status'); });
      _geolocator.checkGeolocationPermissionStatus(locationPermission: GeolocationPermission.locationAlways).then((status) { print('always status: $status'); });
      _geolocator.checkGeolocationPermissionStatus(locationPermission: GeolocationPermission.locationWhenInUse)..then((status) { print('whenInUse status: $status'); });
    }

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

      _geolocator = Geolocator();
      LocationOptions locationOptions = LocationOptions(accuracy: LocationAccuracy.high, distanceFilter: 1);

      checkPermission();
  //    updateLocation();

      StreamSubscription positionStream = _geolocator.getPositionStream(locationOptions).listen(
              (Position position) {
            _position = position;
          });
    }

    void updateLocation() async {
      try {
        Position newPosition = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
            .timeout(new Duration(seconds: 5));

        setState(() {
          _position = newPosition;
        });
      } catch (e) {
        print('Error: ${e.toString()}');
      }
    }

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Startup Name Generator'),
        ),
        body: Center(
            child: Text(
                'Latitude: ${_position != null ? _position.latitude.toString() : '0'},'
                    ' Longitude: ${_position != null ? _position.longitude.toString() : '0'}'
            )
        ),
      );
    }
  }

  class GeolocationExample extends StatefulWidget {
    @override
    GeolocationExampleState createState() => new GeolocationExampleState();
  }

What is the future of Flutter? Flutter going to kill React Native?

What is the future of Flutter? Flutter going to kill React Native?

What is the future of Flutter? Will Flutter kill React Native? What are the benefits that Flutter provides over other popular frameworks?

An Overview of the two most popular and trending frameworks for Hybrid Application Development. Introduction

Hybrid Application development is ruling the Mobile Application development industry rather than Native Development from a couple of years onwards. Business needs are changing from day to day because of an increase in growth and demand. One of the most popular frameworks that became a friend for most of the mobile application developers is React Native, which was introduced by Facebook in the year 2015. until 2015 there was no perfect framework for hybrid application development even though Phone Gap was available in the market. Phone Gap is based on the C# programming language. Developers didn’t love that framework much because there are certain disadvantages in Phone Gap.

The Reasons for the Increase in Demand for Hybrid Application Development

Developing a Mobile Application is really fundamental for the business, but developing the applications for different mobile platforms is becoming an issue for the business investors because of the following problems

  1. They need to maintain different developing teams for different platforms which might lead to a waste of time and money.
  2. It is difficult to find skilled developers, especially in the Asian countries like India, China, Bangladesh, Russia etc… because Apple has a low market in the Asian countries.
  3. There is a development gap between some of the more popular platforms. Deciding which platform to use or focus on can cause confusion.

The above reasons gave birth to the idea of creating cross-platform mobile applications.

There are lots of solutions available in the market but the most popular and trending one right now is by creating compiled apps that can run on both IOS and Android platforms without creating any issues by providing a good performance to the user. The most powerful competitors in that field at this moment are Flutter and React Native. Let’s discuss the strengths and weaknesses of both Flutter and React Native and perform an objective comparison on React Native and Flutter I’ll give the conclusion weather React Native is going to die or not.

React Native

React Native is a powerful javascript framework developed by Facebook for developing cool amazing applications within a short period of time, which is having a capability to run on both IOS and Android platforms. It is developed based on React, it is a Facebook’s JavaScript library for building user interfaces — but instead of targeting the Web Platform, it targets on mobile platforms.

Flutter

Flutter is a new Hybrid mobile application development framework developed by Google and released in the year 2017. It is built and based on the compiled programming language called Dart, which is agoogle’s general-purpose programming language released in the year 2011. this newly introduced framework will help us to build a variety of critical applications within a short period of time.

One of the advantages of Flutter is that it uses Dart as a programming language, the applications that build with the help of Flutter is able to run on both Android and IOS platform. the applications that are built with the help of Flutter are ridiculously fast in performance.

The Differences Between React Native and Flutter

Till now I’ve completed the introduction of React Native and Flutter and also I explained about why Hybrid Application development is in demand, let’s look at the key differences with React Native and Flutter.

User Interface

React Native

React Native’s UI experience is like using HTML without any CSS framework. While that sounds weird, it actually helps you to build on the native components for both Android and iOS, which provides a better user experience (UX). There is also huge community support, and there are third-party libraries that let you get off the ground in no time.

Flutter

Flutter has built-in UI elements which are probably called as widgets — All the required shapes, graphics, animations that are required for an application will be created with the help of widgets. If you want to customize your UI easily and quickly, you can use Material Design widgets in your Android application development and Cupertino widgets for IOS application development. However, there is limited support to the community for building and maintaining these widgets. As the framework matures, widgets will be the way to quickly iterate and build Flutter apps.

Performance

React Native

Applications that are built with the help of React Native is having an issue with rendering large dataset because React application runs with the help of javascript bridge, we can improve the speed of an application with the use of third-party libraries. However, in the race of a competitive world, React Native can’t able to beat flutter in terms of performance.

Flutter

Applications that are developed with the help of flutter are having the greatest speed over React Native because it is having an advantage of using Dart. Dart is one of the fastest compiled based languages in the world. Flutter applications don’t require any intermediate bridges to run the application like React Native that’s why Flutter is more powerful than React Native.

Development Time

React Native

React native is having several third-party libraries to design a good user interface.it does not require you to write and add separate files for complex UI support, which reduces the development time and improves workflow for the developers. with the help of React Native, developers can develop the cross-platform applications very quickly within a short period of time.

Flutter

The biggest disadvantage of flutter is that we need to add separate code files for Android and IOS platforms when building complex UI elements. To customize the UI elements we require Dart programming language, Developers need to keep a lot of effort for designing the UI for an application. But once the UI is designed with good looking features then automatically it will give better user experience in both platforms rather than Native Applications.

Developers usually say that Applications that are built with the help of Flutter will give more user experience than Native Applications.

Community Support

React Native

React Native’s community support already exceeded Flutter’s — React Native is currently the most popular framework on Stack Overflow, with community support as proof. React Native is completely open source. there are many blogs and forums to help for beginner programmers.

Flutter

Flutter was released in the year 2017. it has been almost 2 years and also there is no much love for the Dart programming language in the StackOverflow community. the community support for the Flutter is quite minimal, but the flutter developers at Google are really good, Flutter documentation is really fantastic and easy to follow for the beginners as well.

Code Structure

React Native

React Native has really done a fantastic job in segregating logic, navigation, and design for an application. This allows you to build out complex mobile applications in an easy manner. this is one of the reasons why developers love towards React Native more than other Frameworks.

Flutter

Flutter doesn’t separate any data, template files, and UI elements. because it uses Dart has a core programming language for building the UI elements even in the complex applications also. So we can simply say that everything is Inline.

Conclusion

React Native currently has steady growth, great community support, amazing code structure, and a really good adoption base for developers. while it comes to Flutter —

It has great potential as part of the future of mobile app development. what I think that

Flutter + Dart + LargeCommunity = Recipe for Success

The only thing that’s stopping most of the developers is that Google’s well-known move of shutting down open source and acquired products.

Hope this article helps you to understand the main difference between Flutter and React Native and the future scope.

Flutter: Adding Bluetooth Functionality

Flutter: Adding Bluetooth Functionality

This article will help you use Bluetooth functionality with Flutter.

This article will help you use Bluetooth functionality with Flutter.

Introduction:

There is little documentation to no documentation on using Bluetooth in Flutter. In this article, I will help you by demonstrating some basic concepts needed to implement Bluetooth functionality in your app.

Firstly, plugin/dependency we will be using in this app to add Bluetooth is “flutter_bluetooth_serial”, this plugin is implemented from another parent plugin called “flutter_blue”. This is a very new plugin, the only plugin for bluetooth available as of now. It contains a few bugs but trust me, this will surely get your job done for most basic projects.

Note: Before we go any further, it is worth mentioning that this plugin will only work for Android### Implementation:

Add this dependency in your “pubspec.yaml” file :

dependencies:
flutter_bluetooth_serial: ^0.0.4

In the “main.dart” file the base code of the app will look like this:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BluetoothApp(), // BluetoothApp() would be defined later 
    );
  }
}

Now, let’s create a StatefulWidget called “BluetoothApp”. In _BluetoothAppState, we need to define some variables and a Key. We also have to get an instance of FlutterBluetoothSerial in this class. This class will allow us to control and retrieve Bluetooth information.

class BluetoothApp extends StatefulWidget {
  @override
  _BluetoothAppState createState() => _BluetoothAppState();
}

class _BluetoothAppState extends State<BluetoothApp> {
  // Initializing a global key, as it would help us in showing a SnackBar later
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  // Get the instance of the bluetooth
  FlutterBluetoothSerial bluetooth = FlutterBluetoothSerial.instance;

  // Define some variables, which will be required later
  List<BluetoothDevice> _devicesList = [];
  BluetoothDevice _device;
  bool _connected = false;
  bool _pressed = false;

  @override
  Widget build(BuildContext context) {
    return Container(
      // We have to work on the UI in this part
    );
  }
}

Now, it’s time for implementing the critical portion of the app. We have to get the list of Paired Bluetooth devices and check whether the Bluetooth is connected. This is done asynchronously. We also have to create a list of devices, to be shown in the UI later.

These operations should be done in a “Future” method, which should be called from initState().

class _BluetoothAppState extends State<BluetoothApp> {
  ...

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

  // We are using async callback for using await
  Future<void> bluetoothConnectionState() async {
    List<BluetoothDevice> devices = [];

    // To get the list of paired devices
    try {
      devices = await bluetooth.getBondedDevices();
    } on PlatformException {
      print("Error");
    }

    // For knowing when bluetooth is connected and when disconnected
    bluetooth.onStateChanged().listen((state) {
      switch (state) {
        case FlutterBluetoothSerial.CONNECTED:
          setState(() {
            _connected = true;
            _pressed = false;
          });

          break;

        case FlutterBluetoothSerial.DISCONNECTED:
          setState(() {
            _connected = false;
            _pressed = false;
          });
          break;

        default:
          print(state);
          break;
      }
    });

    // It is an error to call [setState] unless [mounted] is true.
    if (!mounted) {
      return;
    }

    // Store the [devices] list in the [_devicesList] for accessing
    // the list outside this class
    setState(() {
      _devicesList = devices;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      // We have to work on the UI in this part
    );
  }
}

Time to move on to the UI , the most beautiful part of Flutter coding. The code would be a little bit long but it would mostly contain easily readable code, if you are somewhat familiar with the Flutter Widgets. After completing this UI, we have to implement some methods.

...
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        key: _scaffoldKey,
        appBar: AppBar(
          title: Text("Flutter Bluetooth"),
          backgroundColor: Colors.deepPurple,
        ),
        body: Container(
          // Defining a Column containing FOUR main Widgets wrapped with some padding:
          // 1. Text
          // 2. Row
          // 3. Card
          // 4. Text (wrapped with "Expanded" and "Padding")
          child: Column(
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.only(top: 8.0),
                child: Text(
                  "PAIRED DEVICES",
                  style: TextStyle(fontSize: 24, color: Colors.blue),
                  textAlign: TextAlign.center,
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                // Defining a Row containing THREE main Widgets:
                // 1. Text
                // 2. DropdownButton
                // 3. RaisedButton
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Text(
                      'Device:',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    DropdownButton(
                      // To be implemented : _getDeviceItems()
                      items: _getDeviceItems(),
                      onChanged: (value) => setState(() => _device = value),
                      value: _device,
                    ),
                    RaisedButton(
                      onPressed:
                          // To be implemented : _disconnect and _connect
                          _pressed ? null : _connected ? _disconnect : _connect, 
                      child: Text(_connected ? 'Disconnect' : 'Connect'),
                    ),
                  ],
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Card(
                  elevation: 4,
                  child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    // Defining a Row containing THREE main Widgets:
                    // 1. Text (wrapped with "Expanded")
                    // 2. FlatButton
                    // 3. FlatButton
                    child: Row(
                      children: <Widget>[
                        Expanded(
                          child: Text(
                            "DEVICE 1",
                            style: TextStyle(
                              fontSize: 20,
                              color: Colors.green,
                            ),
                          ),
                        ),
                        FlatButton(
                          onPressed:
                              // To be implemented : _sendOnMessageToBluetooth()
                              _connected ? _sendOnMessageToBluetooth : null,
                          child: Text("ON"),
                        ),
                        FlatButton(
                          onPressed:
                              // To be implemented : _sendOffMessageToBluetooth()
                              _connected ? _sendOffMessageToBluetooth : null,
                          child: Text("OFF"),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.all(20),
                  child: Center(
                    child: Text(
                      "NOTE: If you cannot find the device in the list, "
                      "please turn on bluetooth and pair the device by "
                      "going to the bluetooth settings",
                      style: TextStyle(
                          fontSize: 15,
                          fontWeight: FontWeight.bold,
                          color: Colors.red),
                    ),
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
}

So, now it’s time for implementing the remaining methods. At first let us start with the _getDeviceItems() method.

  ...
  // Create the List of devices to be shown in Dropdown Menu
  List<DropdownMenuItem<BluetoothDevice>> _getDeviceItems() {
    List<DropdownMenuItem<BluetoothDevice>> items = [];
    if (_devicesList.isEmpty) {
      items.add(DropdownMenuItem(
        child: Text('NONE'),
      ));
    } else {
      _devicesList.forEach((device) {
        items.add(DropdownMenuItem(
          child: Text(device.name),
          value: device,
        ));
      });
    }
    return items;
}

With the UI out of the way, we are left with four methods. For this example, we will be implementing the connect and disconnect methods. We’ll also implement a method to display a “SnackBar” to the user if there are no Bluetooth device is selected when the user tries to connect.

...
// Method to connect to bluetooth
  void _connect() {
    if (_device == null) {
      show('No device selected');
    } else {
      bluetooth.isConnected.then((isConnected) {
        if (!isConnected) {
          bluetooth
              .connect(_device)
              .timeout(Duration(seconds: 10))
              .catchError((error) {
            setState(() => _pressed = false);
          });
          setState(() => _pressed = true);
        }
      });
    }
  }

  // Method to disconnect bluetooth
  void _disconnect() {
    bluetooth.disconnect();
    setState(() => _pressed = true);
  }
  
  // Method to show a Snackbar,
  // taking message as the text
  Future show(
    String message, {
    Duration duration: const Duration(seconds: 3),
  }) async {
    await new Future.delayed(new Duration(milliseconds: 100));
    _scaffoldKey.currentState.showSnackBar(
      new SnackBar(
        content: new Text(
          message,
        ),
        duration: duration,
      ),
    );
  }
...

At this point, we are almost finished. We are now left with two methods, one for sending a message to turn on Bluetooth and the other for sending a message to turn off Bluetooth.

  ...
  // Method to send message,
  // for turning the bletooth device on
  void _sendOnMessageToBluetooth() {
    bluetooth.isConnected.then((isConnected) {
      if (isConnected) {
        bluetooth.write("1");
        show('Device Turned On');
      }
    });
  }

  // Method to send message,
  // for turning the bletooth device off
  void _sendOffMessageToBluetooth() {
    bluetooth.isConnected.then((isConnected) {
      if (isConnected) {
        bluetooth.write("0");
        show('Device Turned Off');
      }
    });
  }
...

That’s it! the Dart code required to make this work is now complete. That said, if we try running our app it will crash:

To fix this, we need to add the sdk to the AndroidManifest. Navigate to your project folder and follow these steps: android -> app -> src -> main -> AndroidManifest.xml

Add these two lines of code in your “AndroidManifest.xml” file :

<manifest ...
    <!-- Add this line (inside manifest tag) -->
    xmlns:tools="http://schemas.android.com/tools">
    
    <!-- and this line (outside manifest tag) -->
    <uses-sdk tools:overrideLibrary="io.github.edufolly.flutterbluetoothserial"/>
    ....

</manifest>

Conclusion:

As I said at the beginning of this article, this plugin contains some bugs and is still under development.

Below are some screenshots showing various phases. If the user doesn’t have permission, the first thing the user will see is a prompt to grant the app location access. This is completely normal, just click “Allow” and everything should be fine.

Screenshots:

You are free to modify the code to add more functionality to the app.

The GitHub repo link for this project is here

If you like this project, please give “Stars” in my GitHub repo. Thank you for reading, if you enjoyed the article make sure to show me some love by hitting that clap button!

Happy coding…

Learn More

Getting started with Flutter

Flutter Tutorial - Flight List UI Example In Flutter

Let’s Develop a Mobile App in Flutter

Mastering styled text in Flutter

A Design Pattern for Flutter

Weather App with “flutter_bloc”

How to integrate your iOS Flutter App with Firebase on MacOS

An introduction to Dart and Flutter

Learn Flutter & Dart to Build iOS & Android Apps

Flutter & Dart - The Complete Flutter App Development Course

Dart and Flutter: The Complete Developer’s Guide

Flutter - Advanced Course

Mastering styled text in Flutter

Mastering styled text in Flutter

In this tutorial we are going to start with an overview of Dart strings and Unicode. Next we’ll move on to styling text for your app, first for entire strings and then for spans within a string.

Introduction

In this tutorial we are going to start with an overview of Dart strings and Unicode. Next we’ll move on to styling text for your app, first for entire strings and then for spans within a string.

Prerequisites

To go through this tutorial you should have the Flutter development environment set up and know how to run an app. I’m using Android Studio with the Flutter 1.1 plugin, which uses Dart 2.1.

Setup

Create a new Flutter app. I’m calling mine flutter_text.

Open main.dart and replace the code with the following:

    import 'package:flutter/material.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            appBar: AppBar(title: Text('Styling text')),
            body: Container(
              child: Center(
                child: _myWidget(context),
              ),
            ),
          ),
        );
      }
    }

    // modify this widget with the example code below
    Widget _myWidget(BuildContext context) {
      String myString = 'I ❤️ Flutter';
      print(myString);
      return Text(
        myString,
        style: TextStyle(fontSize: 30.0),
      );
    }

Note the _myWidget() function at the end. You can modify or replace it using the examples below. The more you experiment on your own, the more you will learn.

If you are already familiar with concepts like grapheme clusters and Dart strings, you can skip down to the text styling sections below.

Unicode

Coded messages

When I was a kid I liked to write “secret” messages in code, where 1=a, 2=b, 3=c and so on until 26=z. A message using this code might be:

    9   12 9 11 5   6 12 21 20 20 5 18


To make the code even more secret you could shift the numbers, where 1=b, 2=c, 3=d and so on until it wrapped around where 26=a. As long as my friend and I had the same code key, we could decode each other’s messages. The wrong code key, though, would give garbled nonsense.

Computers are similar, except most of the time we don’t want secret messages. We want to make our messages easy to decode, so we agree on a code key, or should I say, a standard. ASCII was an early example of this, where the code key was 97=a, 98=b, 99=c, and so on. That worked fine for English but ASCII only had 128 codes (from 7 bits of data) and that wasn’t enough for all of the characters in other languages. So people made other code keys with more numbers. The problem was that the numbers overlapped and when you used the wrong decoding key you ended up with garbled nonsense.

Unicode to the rescue

Unicode is an international standard that assigns unique code numbers for the characters of every language in the world. The code numbers are called code points. In addition to what we normally think of as characters, there are also code points for control characters (like a new line), diacritical marks (like the accent over an é), and pictures (like 😊). As long as everyone agrees to use this code standard, there are no more fewer garbled messages.

Unicode is just a long list of code points. Saving these code points or sending them is another matter. To help you understand this, take my secret message from above as an example. If I write it as a string of numbers without whitespace and try to send it to you, you get:

    9129115612212020518


This is almost impossible to decode now. Does 912 mean 9, 1, 2 or does it mean 9, 12? It’s the same situation with Unicode. We have to use an agreed upon means to save and send Unicode text, or else it would be very difficult to decode. There are three main ways to do it: UTF-8, UTF-16, and UTF-32. UTF stands for Unicode Transformation Format, and each method of encoding has its advantages and disadvantages.

  • UTF-8 saves each code point using one to four bytes of data.
  • UTF-16 saves each code point as two or four bytes of data. One 16-bit code unit is big enough to uniquely reference a lot of Unicode code points, but not big enough for all of them (emojis, for example). In order to save code points with numbers higher than 16 bits (that is, higher than the number 65,535), UTF-16 uses two 16-bit code units (called surrogate pairs) to map the other code points.
  • UTF-32 saves each code point using four bytes of data. It provides a direct one-to-one mapping of UTF-32 code units to Unicode code points.

When working with UTF-16 code units, you need to be careful not to forget about the other half of a surrogate pair. And even if you are working with UTF-32, you shouldn’t assume that a single code point is the same as what a user perceives to be a character. For example, country flags (like 🇨🇦) are made of two code points. An accented character (like é) can also optionally be made from two code points. In addition to this, there are emoji with skin tone (like 👩🏾, 2 code points) and family emoji (like 👨‍👩‍👧, 5 code points).

So as a programmer, it is better not to think of UTF code units or Unicode code points as characters themselves. That will lead to bugs (for example, when trying to move the cursor one place to the left). Instead, you should think about what Unicode calls a grapheme cluster. These are user-perceived characters. So 🇨🇦, é, 👩🏾, and 👨‍👩‍👧 are each a single grapheme cluster because they each look like a single character even though they are made up of multiple Unicode code points.

Further reading

If you find this interesting or would like a deeper understand of the issues related to Unicode, I encourage you to read the following articles:

  • UTF-8 saves each code point using one to four bytes of data.
  • UTF-16 saves each code point as two or four bytes of data. One 16-bit code unit is big enough to uniquely reference a lot of Unicode code points, but not big enough for all of them (emojis, for example). In order to save code points with numbers higher than 16 bits (that is, higher than the number 65,535), UTF-16 uses two 16-bit code units (called surrogate pairs) to map the other code points.
  • UTF-32 saves each code point using four bytes of data. It provides a direct one-to-one mapping of UTF-32 code units to Unicode code points.
Dart strings

Let’s move on from talking about Unicode in a general way to seeing how Dart uses it.

Code units

In Dart, strings are sequences of UTF-16 code units. That makes string manipulation look deceptively easy because you can get the string value of a code unit by a random integer index:

    String myString = 'Flutter';
    String myChar = myString[0]; // F

But this creates bugs if you split a surrogate pair.

    String myString = '🍎';                    // apple emoji
    List<int> codeUnits = myString.codeUnits;  // [55356, 57166]
    String myChar = myString[0];               // 55356 (half of a surrogate pair)

This will throw an exception if you try to display myChar in a Text widget.

Runes

A better alternative is to work with code points, which are called runes in Dart.

    String myString = '🍎π';

    List<int> codeUnits = myString.codeUnits;    // [55356, 57166, 960]
    int numberOfCodeUnits = myString.length;     // 3
    int firstCodeUnit = myString.codeUnitAt(0);  // 55356

    Runes runes = myString.runes;                // (127822, 960)
    int numberOfCodPoints = runes.length;        // 2
    int firstCodePoint = runes.first;            // 127822

Grapheme clusters

Even runes will fail when you have grapheme clusters composed of multiple code points.

    String myString = '🇨🇦';
    Runes runes = myString.runes;                // (127464, 127462)
    int numberOfCodePoints = runes.length;       // 2
    int firstCodePoint = runes.first;            // 127464
    String halfFlag = String.fromCharCode(firstCodePoint); // 🇨

Displaying the halfFlag string in your app won’t crash it, but users will perceive it as a bug since it only contains one of the two regional indicator symbols used to make the Canadian flag.

Unfortunately, at the time of this writing, there is no support for grapheme clusters in Dart, though there is talk of implementing it. You should still keep them in mind while writing tests and working with strings, though.

Hexadecimal notation

If you are starting with a Unicode hex value, this is how you get a string:

    String s1 = '\u0043';                // C
    String s2 = '\u{43}';                // C
    String s3 = '\u{1F431}';             // 🐱 (cat emoji)
    String s4 = '\u{65}\u{301}\u{20DD}'; //  é⃝ = "e" + accent mark + circle
    int charCode = 0x1F431;              // 🐱 (cat emoji)
    String s5 = String.fromCharCode(charCode);

Substrings

The String documentation (here and here) is pretty good, and you should read it if you haven’t already. I want to review substrings before we go on to text styling, though, since we will be using it later.

To get a substring you do the following:

    String myString = 'I ❤️ Flutter.';
    int startIndex = 5;
    int endIndex = 12;
    String mySubstring = myString.substring(startIndex, endIndex); // Flutter

You can find index numbers with indexOf():

    int startIndex = myString.indexOf('Flutter');

OK, that’s enough background information. Let’s get on to styling text in Flutter.

Text styling with the Text widget

We are going to look first at styling strings in a Text widget. After that we will see how to style substrings within a RichText widget. Both of these widgets use a TextStyle widget to hold the styling information.

Replace _myWidget() with the following code:

    Widget _myWidget(BuildContext context) {
      return Text(
        'Styling text in Flutter',
        style: TextStyle(
          fontSize: 30.0,
        ),
      );
    }

Or, if you would like to compare multiple style settings at once, you can use the following column layout.

    Widget _myWidget(BuildContext context) {
      return Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            'Styling text in Flutter',
            style: TextStyle(
              fontSize: 8,
            ),
          ),
          Text(
            'Styling text in Flutter',
            style: TextStyle(
              fontSize: 12,
            ),
          ),
          Text(
            'Styling text in Flutter',
            style: TextStyle(
              fontSize: 16,
            ),
          ),
        ],
      );
    }

Note that I am setting the TextStyle using the style property of the Text widget. I will modify the TextStyle options below. Try them out yourself by pressing hot reload between every change. You may want to leave a large font size (like fontSize: 30) for some of the later examples below so that you can see what is happening.

Text size

    TextStyle(
      fontSize: 30.0,
    )

When fontSize is not given, the default size is 14 logical pixels. Logical pixels are independent of a device’s density. That is, the text should appear to be to be basically the same size no matter what the pixel density of a user’s device may be. However, this font size is also multiplied by a textScaleFactor depending on the user’s preferred font size.

If you wish to disable accessibility scaling, you can set it on the Text widget. (I’m very impressed that Flutter has accessibility enabled by default, and I definitely don’t suggest that you disable it without reason. In some rare cases, though, an oversized font might break a layout…in which case it would still probably be better to redesign your layout rather than disable accessibility.)

    // This text will always display at 30.0 logical pixels, no matter
    // what the user's preferred size is.
    Text(
      'Some text',
      textScaleFactor: 1.0, // disables accessibility
      style: TextStyle(
        fontSize: 30.0
      ),
    )

You can also use the theme data to set the text size. See the section on themes below.

Text color

    TextStyle(
      color: Colors.green,
    )

In addition to predefined colors like Colors.green and Colors.red, you can also set shades on a color, like Colors.blue[100] or Colors.blue[700].

Background color

    Widget _myWidget(BuildContext context) {
      Paint paint = Paint();
      paint.color = Colors.green;
      return Text(
        'Styling text in Flutter',
        style: TextStyle(
          background: paint,
          fontSize: 30.0,
        ),
      );
    }

For a Text widget you could also just wrap it in a Container and set the color on the Container.

Bold

    TextStyle(
      fontWeight: FontWeight.bold,
    )

You can set the weight with numbers like FontWeight.w100 where w400 is the same as normal and w700 is the same as bold.

Italic

    TextStyle(
      fontStyle: FontStyle.italic,
    )

The only choices are italic and normal.

Shadow

    TextStyle(
      shadows: [
        Shadow(
          blurRadius: 10.0,
          color: Colors.blue,
          offset: Offset(5.0, 5.0),
        ),
      ],
    )

When setting the shadow you can change the blur radius (bigger means more blurry), color, and offset. You can even set multiple shadows as if there were more than one light source.

    TextStyle(
      shadows: [
        Shadow(
          color: Colors.blue,
          blurRadius: 10.0,
          offset: Offset(5.0, 5.0),
        ),
        Shadow(
          color: Colors.red,
          blurRadius: 10.0,
          offset: Offset(-5.0, 5.0),
        ),
      ],
    )

I’m not sure if more than one shadow is useful or not, but it is interesting.

Underline

    TextStyle(
      decoration: TextDecoration.underline,
      decorationColor: Colors.black,
      decorationStyle: TextDecorationStyle.solid,
    )

The decoration can be underline, lineThrough, or overline. The last line of text in the image above has an overline.

The choices for decorationStyle are solid, double, dashed, dotted, and wavy.

Spacing

    TextStyle(
      letterSpacing: -1.0,
      wordSpacing: 5.0,
    )

In the example image, the six lines on top use letter spacing ranging from -2.0 to 3.0. The six lines on bottom use word spacing ranging from -3.0 to 12.0. A negative value moves the letters or words closer together.

Font

Using a custom font requires a few more steps:

  1. Add a directory called assets to the root of your project.
  2. Copy a font into it. (I downloaded the Dancing Script font from here, unzipped it, and renamed the regular one to dancing_script.ttf.)
  3. In pubspec.yaml register the font:
    flutter:
      fonts:
      - family: DancingScript
        fonts:
        - asset: assets/dancing_script.ttf

  1. Add a directory called assets to the root of your project.
  2. Copy a font into it. (I downloaded the Dancing Script font from here, unzipped it, and renamed the regular one to dancing_script.ttf.)
  3. In pubspec.yaml register the font:
    TextStyle(
      fontFamily: 'DancingScript',
    )

  1. Add a directory called assets to the root of your project.
  2. Copy a font into it. (I downloaded the Dancing Script font from here, unzipped it, and renamed the regular one to dancing_script.ttf.)
  3. In pubspec.yaml register the font:

See this post for more help.

Using themes

Our root widget is a MaterialApp widget, which uses the Material Design theme. Through the BuildContext we have access to its predefined text styles. Instead of creating our own style with TextStyle, you can use a default one like this:

    Text(
      'Styling text in Flutter',
      style: Theme.of(context).textTheme.title,
    )

That was the default style for titles. There are many more defaults for other types of text. Check them out:

If a style is not specified, Text uses the DefaultTextStyle. You can use it yourself like this:

    Text(
      'default',
      style: DefaultTextStyle.of(context).style,
    )

DefaultTextStyle gets its style from the build context.

See the documentation for more about using themes.

Text styling with the RichText widget

The final thing I want to teach you is how to style part of a text string. With a Text widget the whole string has the same style. A RichText widget, though, allows us to add TextSpans that include different styles.

Basic example

Replace _myWidget() with the following code:

    Widget _myWidget(BuildContext context) {
      return RichText(
        text: TextSpan(
          // set the default style for the children TextSpans
          style: Theme.of(context).textTheme.body1.copyWith(fontSize: 30),
          children: [
            TextSpan(
                text: 'Styling ',
            ),
            TextSpan(
              text: 'text',
              style: TextStyle(
                color: Colors.blue
              )
            ),
            TextSpan(
                text: ' in Flutter',
            ),
          ]
        )
      );
    }

Note: An alternate way to make text with styled spans is to use the Text.rich() constructor, which has the same default style as the Text widget.
RichText takes a TextSpan tree. Every very TextSpan takes more TextSpan children, which inherit the style of their parent. To make the word “text” blue, I had to divide the string into three TextSpans. I used a color for the style, but I could have just as easily used any of the other styles that we have already looked at. Try adding a few more styles yourself.

Styling programmatically

In a real application we would probably have a longer string. For example, let’s highlight every occurrence of “text” in the following string:

To do that we have to look at the string and find the indexes of the text that we want to style. Then we use substring to cut the string up and put it in a list of TextSpans.

Replace _myWidget() with the following code:

    Widget _myWidget(BuildContext context) {

      final String myString =
          'Styling text in Flutter Styling text in Flutter '
          'Styling text in Flutter Styling text in Flutter '
          'Styling text in Flutter Styling text in Flutter '
          'Styling text in Flutter Styling text in Flutter '
          'Styling text in Flutter Styling text in Flutter ';

      final wordToStyle = 'text';
      final style = TextStyle(color: Colors.blue);
      final spans = _getSpans(myString, wordToStyle, style);

      return RichText(
        text: TextSpan(
          style: Theme.of(context).textTheme.body1.copyWith(fontSize: 30),
          children: spans,
        ),
      );
    }

    List<TextSpan> _getSpans(String text, String matchWord, TextStyle style) {

      List<TextSpan> spans = [];
      int spanBoundary = 0;

      do {

        // look for the next match
        final startIndex = text.indexOf(matchWord, spanBoundary);

        // if no more matches then add the rest of the string without style
        if (startIndex == -1) {
          spans.add(TextSpan(text: text.substring(spanBoundary)));
          return spans;
        }

        // add any unstyled text before the next match
        if (startIndex > spanBoundary) {
          spans.add(TextSpan(text: text.substring(spanBoundary, startIndex)));
        }

        // style the matched text
        final endIndex = startIndex + matchWord.length;
        final spanText = text.substring(startIndex, endIndex);
        spans.add(TextSpan(text: spanText, style: style));

        // mark the boundary to start the next search from
        spanBoundary = endIndex;

      // continue until there are no more matches
      } while (spanBoundary < text.length);

      return spans;
    }

Experiment with changing the search word and style.

In this example we searched for plain text, but you can also do pattern matching using regular expressions.

Clickable spans

You can make a span clickable by adding a TapGestureRecognizer:

    TextSpan(
      text: spanText,
      style: style,
      recognizer: TapGestureRecognizer()
        ..onTap = () {
          // do something
        },
    )

This would allow you to open a URL, for example, if used along with the url_launcher plugin.

Final notes

Here are a few more related concepts that I didn’t have time or space to cover:

  • UTF-8 saves each code point using one to four bytes of data.
  • UTF-16 saves each code point as two or four bytes of data. One 16-bit code unit is big enough to uniquely reference a lot of Unicode code points, but not big enough for all of them (emojis, for example). In order to save code points with numbers higher than 16 bits (that is, higher than the number 65,535), UTF-16 uses two 16-bit code units (called surrogate pairs) to map the other code points.
  • UTF-32 saves each code point using four bytes of data. It provides a direct one-to-one mapping of UTF-32 code units to Unicode code points.
Conclusion

Text seems like it should be so simple, but it really isn’t. Language is messy and dealing with it as a programmer can be difficult. Much progress has been made in recent years, though. Unicode has solved a lot of problems. Dart and Flutter also give us a lot of tools to manipulate and style text. I expect to see these tools improve even more in the future.

The source code for this project is available on GitHub.

By the way, in case you were curious but lazy, my secret message was “I like Flutter”.