how to fix "Fatal error: Unexpectedly found nil while unwrapping an Optional value" in Xcode

 @IBAction func DoneButton(_ sender: Any) {

 @IBAction func DoneButton(_ sender: Any) {


       print("Done Clicked")

       DissmissKeyboard()

       ProgressHUD.show("Registering.....")



       if NameTextField.text != "" && SurnameTextField.text != "" && CountrYTextField.text != "" && CityTextField.text != "" && PhoneTextField.text != "" {


           FUser.registerUserWith(email: Email!, password: Password!, firstName: NameTextField.text!, lastName: SurnameTextField.text!) { (error) in


               if error != nil {

                   ProgressHUD.dismiss()

                   ProgressHUD.showError(error!.localizedDescription)

                   return

               }


               self.RegisterUser()

           }



       } else {

           ProgressHUD.showError("All Fields Are Required..")

       }

   }



Fatal error: Unexpectedly found nil while unwrapping an Optional value

Laravel 5.8 Ajax CRUD tutorial using Datatable JS

Laravel 5.8 Ajax CRUD tutorial using Datatable JS

In this tutorial, i want to share with you create jquery ajax crud operations application using datatable js, modals in laravel 5.8. we will create insert update delete records with modal and pagination in laravel 5.8.

In this tutorial, i want to share with you create jquery ajax crud operations application using datatable js, modals in laravel 5.8. we will create insert update delete records with modal and pagination in laravel 5.8.

We will use yajra datatable to list a records with pagination, sorting and filter (search). we will use bootstrap modal for create new records and update new records. we will use resource routes to create crud (create read update delete) application in laravel 5.8.

I will provide you step by step guide to create ajax crud example with laravel 5.8. you just need to follow few step to get c.r.u.d with modals and ajax. you can easily use with your laravel 5.8 project and easy to customize it.

You can see bellow preview of ajax crud app.

List Page

Create Page

Edit Page

Step 1 : Install Laravel 5.8

first of all we need to get fresh Laravel 5.8 version application using bellow command, So open your terminal OR command prompt and run bellow command:

composer create-project --prefer-dist laravel/laravel blog

Step 2 : Install Yajra Datatable Package

We need to install yajra datatable composer package for datatable, so you can install using following command:

composer require yajra/laravel-datatables-oracle

After that you need to set providers and alias.

config/app.php

'providers' => [

	....

	Yajra\DataTables\DataTablesServiceProvider::class,

]

'aliases' => [

	....

	'DataTables' => Yajra\DataTables\Facades\DataTables::class,

]

Step 3: Update Database Configuration

In second step, we will make database configuration for example database name, username, password etc for our crud application of laravel 5.8. So let’s open .env file and fill all details like as bellow:

.env

DB_CONNECTION=mysql

DB_HOST=127.0.0.1

DB_PORT=3306

DB_DATABASE=here your database name(blog)

DB_USERNAME=here database username(root)

DB_PASSWORD=here database password(root)

Step 4: Create Table

we are going to create ajax crud application for product. so we have to create migration for “products” table using Laravel 5.8 php artisan command, so first fire bellow command:

php artisan make:migration create_products_table --create=products

After this command you will find one file in following path “database/migrations” and you have to put bellow code in your migration file for create products table.

<?php

 

use Illuminate\Support\Facades\Schema;

use Illuminate\Database\Schema\Blueprint;

use Illuminate\Database\Migrations\Migration;

  

class CreateProductsTable extends Migration

{

    /**

     * Run the migrations.

     *

     * @return void

     */

    public function up()

    {

        Schema::create('products', function (Blueprint $table) {

            $table->increments('id');

            $table->string('name');

            $table->text('detail');

            $table->timestamps();

        });

    }

  

    /**

     * Reverse the migrations.

     *

     * @return void

     */

    public function down()

    {

        Schema::dropIfExists('products');

    }

}

Now you have to run this migration by following command:

php artisan migrate

Step 5: Create Resource Route

Here, we need to add resource route for product ajax crud application. so open your “routes/web.php” file and add following route.

routes/web.php

Route::resource('ajaxproducts','ProductAjaxController');

Step 6: Create Controller and Model

In this step, now we should create new controller as ProductAjaxController. So run bellow command and create new controller.

So, let’s copy bellow code and put on ProductAjaxController.php file.

app/Http/Controllers/ProductAjaxController.php

<?php

         

namespace App\Http\Controllers;

          

use App\Product;

use Illuminate\Http\Request;

use DataTables;

        

class ProductAjaxController extends Controller

{

    /**

     * Display a listing of the resource.

     *

     * @return \Illuminate\Http\Response

     */

    public function index(Request $request)

    {

   

        if ($request->ajax()) {

            $data = Product::latest()->get();

            return Datatables::of($data)

                    ->addIndexColumn()

                    ->addColumn('action', function($row){

   

                           $btn = '<a href="javascript:void(0)" data-toggle="tooltip"  data-id="'.$row->id.'" data-original-title="Edit" class="edit btn btn-primary btn-sm editProduct">Edit</a>';

   

                           $btn = $btn.' <a href="javascript:void(0)" data-toggle="tooltip"  data-id="'.$row->id.'" data-original-title="Delete" class="btn btn-danger btn-sm deleteProduct">Delete</a>';

    

                            return $btn;

                    })

                    ->rawColumns(['action'])

                    ->make(true);

        }

      

        return view('productAjax',compact('products'));

    }

     

    /**

     * Store a newly created resource in storage.

     *

     * @param  \Illuminate\Http\Request  $request

     * @return \Illuminate\Http\Response

     */

    public function store(Request $request)

    {

        Product::updateOrCreate(['id' => $request->product_id],

                ['name' => $request->name, 'detail' => $request->detail]);        

   

        return response()->json(['success'=>'Product saved successfully.']);

    }

    /**

     * Show the form for editing the specified resource.

     *

     * @param  \App\Product  $product

     * @return \Illuminate\Http\Response

     */

    public function edit($id)

    {

        $product = Product::find($id);

        return response()->json($product);

    }

  

    /**

     * Remove the specified resource from storage.

     *

     * @param  \App\Product  $product

     * @return \Illuminate\Http\Response

     */

    public function destroy($id)

    {

        Product::find($id)->delete();

     

        return response()->json(['success'=>'Product deleted successfully.']);

    }

}

Ok, so after run bellow command you will find “app/Product.php” and put bellow content in Product.php file:

app/Product.php

<?php

  

namespace App;

  

use Illuminate\Database\Eloquent\Model;

   

class Product extends Model

{

    protected $fillable = [

        'name', 'detail'

    ];

}

Step 7: Create Blade Files

In last step. In this step we have to create just blade file. so we need to create only one blade file as productAjax.blade.php file.

So let’s just create following file and put bellow code.

resources/views/productAjax.blade.php

<!DOCTYPE html>

<html>

<head>

    <title>Laravel 5.8 Ajax CRUD tutorial using Datatable - ItSolutionStuff.com</title>

    <meta name="csrf-token" content="{{ csrf_token() }}">

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" />

    <link href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" rel="stylesheet">

    <link href="https://cdn.datatables.net/1.10.19/css/dataTables.bootstrap4.min.css" rel="stylesheet">

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script>  

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.js"></script>

    <script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>

    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>

    <script src="https://cdn.datatables.net/1.10.19/js/dataTables.bootstrap4.min.js"></script>

</head>

<body>

    

<div class="container">

    <h1>Laravel 5.8 Ajax CRUD tutorial using Datatable - ItSolutionStuff.com</h1>

    <a class="btn btn-success" href="javascript:void(0)" id="createNewProduct"> Create New Product</a>

    <table class="table table-bordered data-table">

        <thead>

            <tr>

                <th>No</th>

                <th>Name</th>

                <th>Details</th>

                <th width="280px">Action</th>

            </tr>

        </thead>

        <tbody>

        </tbody>

    </table>

</div>

   

<div class="modal fade" id="ajaxModel" aria-hidden="true">

    <div class="modal-dialog">

        <div class="modal-content">

            <div class="modal-header">

                <h4 class="modal-title" id="modelHeading"></h4>

            </div>

            <div class="modal-body">

                <form id="productForm" name="productForm" class="form-horizontal">

                   <input type="hidden" name="product_id" id="product_id">

                    <div class="form-group">

                        <label for="name" class="col-sm-2 control-label">Name</label>

                        <div class="col-sm-12">

                            <input type="text" class="form-control" id="name" name="name" placeholder="Enter Name" value="" maxlength="50" required="">

                        </div>

                    </div>

     

                    <div class="form-group">

                        <label class="col-sm-2 control-label">Details</label>

                        <div class="col-sm-12">

                            <textarea id="detail" name="detail" required="" placeholder="Enter Details" class="form-control"></textarea>

                        </div>

                    </div>

      

                    <div class="col-sm-offset-2 col-sm-10">

                     <button type="submit" class="btn btn-primary" id="saveBtn" value="create">Save changes

                     </button>

                    </div>

                </form>

            </div>

        </div>

    </div>

</div>

    

</body>

    

<script type="text/javascript">

  $(function () {

     

      $.ajaxSetup({

          headers: {

              'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')

          }

    });

    

    var table = $('.data-table').DataTable({

        processing: true,

        serverSide: true,

        ajax: "{{ route('ajaxproducts.index') }}",

        columns: [

            {data: 'DT_RowIndex', name: 'DT_RowIndex'},

            {data: 'name', name: 'name'},

            {data: 'detail', name: 'detail'},

            {data: 'action', name: 'action', orderable: false, searchable: false},

        ]

    });

     

    $('#createNewProduct').click(function () {

        $('#saveBtn').val("create-product");

        $('#product_id').val('');

        $('#productForm').trigger("reset");

        $('#modelHeading').html("Create New Product");

        $('#ajaxModel').modal('show');

    });

    

    $('body').on('click', '.editProduct', function () {

      var product_id = $(this).data('id');

      $.get("{{ route('ajaxproducts.index') }}" +'/' + product_id +'/edit', function (data) {

          $('#modelHeading').html("Edit Product");

          $('#saveBtn').val("edit-user");

          $('#ajaxModel').modal('show');

          $('#product_id').val(data.id);

          $('#name').val(data.name);

          $('#detail').val(data.detail);

      })

   });

    

    $('#saveBtn').click(function (e) {

        e.preventDefault();

        $(this).html('Sending..');

    

        $.ajax({

          data: $('#productForm').serialize(),

          url: "{{ route('ajaxproducts.store') }}",

          type: "POST",

          dataType: 'json',

          success: function (data) {

     

              $('#productForm').trigger("reset");

              $('#ajaxModel').modal('hide');

              table.draw();

         

          },

          error: function (data) {

              console.log('Error:', data);

              $('#saveBtn').html('Save Changes');

          }

      });

    });

    

    $('body').on('click', '.deleteProduct', function () {

     

        var product_id = $(this).data("id");

        confirm("Are You sure want to delete !");

      

        $.ajax({

            type: "DELETE",

            url: "{{ route('ajaxproducts.store') }}"+'/'+product_id,

            success: function (data) {

                table.draw();

            },

            error: function (data) {

                console.log('Error:', data);

            }

        });

    });

     

  });

</script>

</html>

Now you can test it by using following command:

php artisan serve

Now you can open bellow URL on your browser:

http://localhost:8000/ajaxproducts

I hope it can help you…

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

Securing RESTful API with Spring Boot, Security, and Data MongoDB

Securing RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB. Previously, we have shown you how to securing Spring Boot, MVC and MongoDB web application. In this tutorial, the secure endpoint will restrict the access from an unauthorized request. Every request to secure endpoint should bring authorization token with it. Of course, there will be an endpoint for login which will get authorization token after successful login.

Table of Contents:

The following software, tools, and frameworks are required for this tutorial:

We assume that you already installed all required software, tools, and frameworks. So, we will not cover how to install that software, tools, and frameworks.

1. Generate a New Spring Boot Gradle Project

To create or generate a new Spring Boot Application or Project, simply go to Spring Initializer. Fill all required fields as below then click on Generate Project button.

The project will automatically be downloaded as a Zip file. Next, extract the zipped project to your java projects folder. On the project folder root, you will find build.gradle file for register dependencies, initially it looks like this.

buildscript {
&nbsp;&nbsp; &nbsp;ext {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;springBootVersion = '2.1.2.RELEASE'
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;repositories {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;mavenCentral()
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;dependencies {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
&nbsp;&nbsp; &nbsp;}
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.djamware'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
&nbsp;&nbsp; &nbsp;mavenCentral()
}

dependencies {
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-security'
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-web'
&nbsp;&nbsp; &nbsp;testImplementation 'org.springframework.boot:spring-boot-starter-test'
&nbsp;&nbsp; &nbsp;testImplementation 'org.springframework.security:spring-security-test'
}

Now, you can work with the source code of this Spring Boot Project using your own IDE or Text Editor. We are using Spring Tool Suite (STS). In STS, import the extracted zipped file as Existing Gradle Project.

Next, we have to add the JWT library to the build.gradle as the dependency. Open and edit build.gradle then add this line to dependencies after other implementation.

implementation 'io.jsonwebtoken:jjwt:0.9.1'

Next, compile the Gradle Project by type this command from Terminal or CMD.

./gradlew compile

Or you can compile directly from STS by right-clicking the project name then choose Gradle -> Refresh Gradle Project. Next, open and edit src/main/resources/application.properties then add these lines.

spring.data.mongodb.database=springmongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

2. Create Product, User and Role Model or Entity Classes

We will be creating all required models or entities for products, user and role. In STS, right-click the project name -> New -> Class. Fill the package with com.djamware.SecurityRest.models, the name with Product, and leave other fields and checkbox as default then click Finish Button.

Next, open and edit src/main/java/com/djamware/SecurityRest/models/Product.java then add this annotation above the class name that will point to MongoDB collection.

@Document(collection = "products")

Inside Product class, add these variables.

@Id
String id;
String prodName;
String prodDesc;
Double prodPrice;
String prodImage;

Add constructors after the variable or fields.

public Product() {
}

public Product(String prodName, String prodDesc, Double prodPrice, String prodImage) {
&nbsp;&nbsp; &nbsp;super();
&nbsp;&nbsp; &nbsp;this.prodName = prodName;
&nbsp;&nbsp; &nbsp;this.prodDesc = prodDesc;
&nbsp;&nbsp; &nbsp;this.prodPrice = prodPrice;
&nbsp;&nbsp; &nbsp;this.prodImage = prodImage;
}

Generate or create Getter and Setter for each field.

public String getId() {
&nbsp;&nbsp; &nbsp;return id;
}

public void setId(String id) {
&nbsp;&nbsp; &nbsp;this.id = id;
}

public String getProdName() {
&nbsp;&nbsp; &nbsp;return prodName;
}

public void setProdName(String prodName) {
&nbsp;&nbsp; &nbsp;this.prodName = prodName;
}

public String getProdDesc() {
&nbsp;&nbsp; &nbsp;return prodDesc;
}

public void setProdDesc(String prodDesc) {
&nbsp;&nbsp; &nbsp;this.prodDesc = prodDesc;
}

public Double getProdPrice() {
&nbsp;&nbsp; &nbsp;return prodPrice;
}

public void setProdPrice(Double prodPrice) {
&nbsp;&nbsp; &nbsp;this.prodPrice = prodPrice;
}

public String getProdImage() {
&nbsp;&nbsp; &nbsp;return prodImage;
}

public void setProdImage(String prodImage) {
&nbsp;&nbsp; &nbsp;this.prodImage = prodImage;
}

Using STS you can organize imports automatically from the menu Source -> Organize Imports then you can see the imports after the package name.

package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

You can do the same way as the above step for User and Role class. Here’s the User class looks like.

package com.djamware.SecurityRest.models;

import java.util.Set;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "users")
public class User {

&nbsp;&nbsp; &nbsp;@Id
&nbsp;&nbsp; &nbsp;private String id;
&nbsp;&nbsp; &nbsp;@Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)
&nbsp;&nbsp; &nbsp;private String email;
&nbsp;&nbsp; &nbsp;private String password;
&nbsp;&nbsp; &nbsp;private String fullname;
&nbsp;&nbsp; &nbsp;private boolean enabled;
&nbsp;&nbsp; &nbsp;@DBRef
&nbsp;&nbsp; &nbsp;private Set<Role> roles;
&nbsp;&nbsp; &nbsp;public String getId() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setId(String id) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.id = id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getEmail() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEmail(String email) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.email = email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getPassword() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setPassword(String password) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.password = password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getFullname() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return fullname;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setFullname(String fullname) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.fullname = fullname;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public boolean isEnabled() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return enabled;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEnabled(boolean enabled) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.enabled = enabled;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public Set<Role> getRoles() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return roles;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setRoles(Set<Role> roles) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.roles = roles;
&nbsp;&nbsp; &nbsp;}

}

And the Role class will be like this.

package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "roles")
public class Role {

&nbsp;&nbsp; &nbsp;@Id
&nbsp; &nbsp; private String id;
&nbsp; &nbsp; @Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)

&nbsp; &nbsp; private String role;
&nbsp;&nbsp; &nbsp;public String getId() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setId(String id) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.id = id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getRole() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return role;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setRole(String role) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.role = role;
&nbsp;&nbsp; &nbsp;}

}

3. Create Product, User and Role Repository Interfaces

Next steps to create Product, User, and Role Repository Interfaces. From the STS, right-click the project name -> New -> Interface then fill all required fields and checkboxes as below before click Finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/repositories/ProductRepository.java then add extends to MongoDB CRUD Repository.

public interface ProductRepository extends CrudRepository<Product, String> {

}

Inside the class name add this method.

@Override
void delete(Product deleted);

Organize all required imports.

import org.springframework.data.repository.CrudRepository;
import com.djamware.SecurityRest.models.Product;

The same way can be applied to User and Role repositories. So, the User Repository Interface will look like this.

package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.User;

public interface UserRepository extends MongoRepository<User, String> {

&nbsp;&nbsp; &nbsp;User findByEmail(String email);
}

And the Role Repository Interface will look like this.

package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.Role;

public interface RoleRepository extends MongoRepository<Role, String> {

&nbsp;&nbsp; &nbsp;Role findByRole(String role);
}

4. Create a Custom User Details Service

To implements authentication using existing User and Role from MongoDB, we have to create a custom user details service. From the STS, right-click the project name -> New -> Class File then fill all required fields and checkboxes as below before clicking the finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/services/CustomUserDetailsService.java then give an annotation above the class name and implement the Spring Security User Details Service.

@Service
public class CustomUserDetailsService implements UserDetailsService {
}

Next, inject all required beans at the first line of the class bracket.

@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Autowired
private PasswordEncoder bCryptPasswordEncoder;

Add a method to find a user by email field.

public User findUserByEmail(String email) {
&nbsp; &nbsp; return userRepository.findByEmail(email);
}

Add a method to save a new user.

public void saveUser(User user) {
&nbsp; &nbsp; user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
&nbsp; &nbsp; user.setEnabled(true);
&nbsp; &nbsp; Role userRole = roleRepository.findByRole("ADMIN");
&nbsp; &nbsp; user.setRoles(new HashSet<>(Arrays.asList(userRole)));
&nbsp; &nbsp; userRepository.save(user);
}

Override the Spring Security User Details to load User by email.

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

&nbsp; &nbsp; User user = userRepository.findByEmail(email);
&nbsp; &nbsp; if(user != null) {
&nbsp; &nbsp; &nbsp; &nbsp; List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
&nbsp; &nbsp; &nbsp; &nbsp; return buildUserForAuthentication(user, authorities);
&nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; throw new UsernameNotFoundException("username not found");
&nbsp; &nbsp; }
}

Add a method to get a set of Roles that related to a user.

private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
&nbsp; &nbsp; Set<GrantedAuthority> roles = new HashSet<>();
&nbsp; &nbsp; userRoles.forEach((role) -> {
&nbsp; &nbsp; &nbsp; &nbsp; roles.add(new SimpleGrantedAuthority(role.getRole()));
&nbsp; &nbsp; });

&nbsp; &nbsp; List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roles);
&nbsp; &nbsp; return grantedAuthorities;
}

Add a method for authentication purpose.

private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
&nbsp; &nbsp; return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}

5. Configure Spring Boot Security Rest

Now, the main purpose of this tutorial is configuring Spring Security Rest. First, we have to create a bean for JWT token generation and validation. Right-click the project name -> New -> Class File. Fill the package name as com.djamware.SecurityRest.configs and the Class name as JwtTokenProvider then click the Finish button. Next, open and edit that newly created class file then give it an annotation above the class name.

@Component
public class JwtTokenProvider {
}

Add variables and injected bean inside the class bracket at the top lines.

@Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";

@Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h

@Autowired
private CustomUserDetailsService userDetailsService;

Add a method for initialization.

@PostConstruct
protected void init() {
&nbsp; &nbsp; secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}

Add a method to create a JWT token.

public String createToken(String username, Set<Role> set) {
&nbsp; &nbsp; Claims claims = Jwts.claims().setSubject(username);
&nbsp; &nbsp; claims.put("roles", set);
&nbsp; &nbsp; Date now = new Date();
&nbsp; &nbsp; Date validity = new Date(now.getTime() + validityInMilliseconds);
&nbsp; &nbsp; return Jwts.builder()//
&nbsp; &nbsp; &nbsp; &nbsp; .setClaims(claims)//
&nbsp; &nbsp; &nbsp; &nbsp; .setIssuedAt(now)//
&nbsp; &nbsp; &nbsp; &nbsp; .setExpiration(validity)//
&nbsp; &nbsp; &nbsp; &nbsp; .signWith(SignatureAlgorithm.HS256, secretKey)//
&nbsp; &nbsp; &nbsp; &nbsp; .compact();
}

Add a method to load User by username.

public Authentication getAuthentication(String token) {
&nbsp; &nbsp; UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
&nbsp; &nbsp; return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}

Add a method to get the username by JWT token.

public String getUsername(String token) {
&nbsp; &nbsp; return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}

Add a method to resolve JWT token from request headers of Authorization that has a Bearer prefix.

public String resolveToken(HttpServletRequest req) {
&nbsp; &nbsp; String bearerToken = req.getHeader("Authorization");
&nbsp; &nbsp; if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
&nbsp; &nbsp; &nbsp; &nbsp; return bearerToken.substring(7, bearerToken.length());
&nbsp; &nbsp; }
&nbsp; &nbsp; return null;
}

Add a method to validate a JWT token.

public boolean validateToken(String token) {
&nbsp; &nbsp; try {
&nbsp; &nbsp; &nbsp; &nbsp; Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
&nbsp; &nbsp; &nbsp; &nbsp; if (claims.getBody().getExpiration().before(new Date())) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; return true;
&nbsp; &nbsp; } catch (JwtException | IllegalArgumentException e) {
&nbsp; &nbsp; &nbsp; &nbsp; throw new JwtException("Expired or invalid JWT token");
&nbsp; &nbsp; }
}

Finally, organize imports like below.

package com.djamware.SecurityRest.configs;

import java.util.Base64;
import java.util.Date;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.djamware.SecurityRest.models.Role;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

Next, create a JWT filter class with the name JwtTokenFilter in configs package that extends Spring GenericFilterBean. Replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.configs;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;

public class JwtTokenFilter extends GenericFilterBean {

&nbsp;&nbsp; &nbsp;private JwtTokenProvider jwtTokenProvider;

&nbsp; &nbsp; public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
&nbsp; &nbsp; &nbsp; &nbsp; this.jwtTokenProvider = jwtTokenProvider;
&nbsp; &nbsp; }

&nbsp; &nbsp; @Override
&nbsp; &nbsp; public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
&nbsp; &nbsp; &nbsp; &nbsp; throws IOException, ServletException {
&nbsp; &nbsp; &nbsp; &nbsp; String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
&nbsp; &nbsp; &nbsp; &nbsp; if (token != null && jwtTokenProvider.validateToken(token)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SecurityContextHolder.getContext().setAuthentication(auth);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; filterChain.doFilter(req, res);
&nbsp; &nbsp; }
}

Next, create a class with the name JwtConfigurer for JWT configuration in configs package then replace all codes with these lines of codes.

package com.djamware.SecurityRest.configs;

import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

&nbsp;&nbsp; &nbsp;private JwtTokenProvider jwtTokenProvider;

&nbsp; &nbsp; public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
&nbsp; &nbsp; &nbsp; &nbsp; this.jwtTokenProvider = jwtTokenProvider;
&nbsp; &nbsp; }

&nbsp; &nbsp; @Override
&nbsp; &nbsp; public void configure(HttpSecurity http) throws Exception {
&nbsp; &nbsp; &nbsp; &nbsp; JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
&nbsp; &nbsp; &nbsp; &nbsp; http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
&nbsp; &nbsp; }
}

Finally, we have to configure the Spring Security by creating a Java class file inside configs package with the name WebSecurityConfig. Give annotations to this class and extends Spring WebSecurityConfigurerAdapter.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}

Inject JWT token provider inside this class.

@Autowired
JwtTokenProvider jwtTokenProvider;

Add an override method to configure Authentication Manager Builder.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
&nbsp;&nbsp; &nbsp;UserDetailsService userDetailsService = mongoUserDetails();
&nbsp;&nbsp; &nbsp;auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());

}

Next, add an override method to configure Spring Security Http Security.

@Override
protected void configure(HttpSecurity http) throws Exception {
&nbsp;&nbsp; &nbsp;http.httpBasic().disable().csrf().disable().sessionManagement()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.antMatchers("/api/auth/login").permitAll().antMatchers("/api/auth/register").permitAll()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.antMatchers("/api/products/**").hasAuthority("ADMIN").anyRequest().authenticated().and().csrf()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.disable().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint()).and()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.apply(new JwtConfigurer(jwtTokenProvider));
}

Next, declare all required beans for this configuration.

@Bean
public PasswordEncoder bCryptPasswordEncoder() {
&nbsp;&nbsp; &nbsp;return new BCryptPasswordEncoder();
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
&nbsp;&nbsp; &nbsp;return super.authenticationManagerBean();
}

@Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
&nbsp;&nbsp; &nbsp;return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;"Unauthorized");
}

@Bean
public UserDetailsService mongoUserDetails() {
&nbsp;&nbsp; &nbsp;return new CustomUserDetailsService();
}

6. Create Product and Authentication Controllers

Now it’s time for REST API endpoint. All RESTful API will be created from each controller. Product controller will handle CRUD endpoint of product and Authentication controller will handle login and register endpoint. Right-click project name -> New -> Class then fill the package with com.djamware.SecurityRest.controllers and the class name as ProductController. Open and edit the newly created class file then replace all codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.models.Product;
import com.djamware.SecurityRest.repositories.ProductRepository;

@RestController
public class ProductController {

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp; &nbsp; ProductRepository productRepository;

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.GET, value="/api/products")
&nbsp; &nbsp; public Iterable<Product> product() {
&nbsp; &nbsp; &nbsp; &nbsp; return productRepository.findAll();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.POST, value="/api/products")
&nbsp; &nbsp; public String save(@RequestBody Product product) {
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.save(product);

&nbsp; &nbsp; &nbsp; &nbsp; return product.getId();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.GET, value="/api/products/{id}")
&nbsp; &nbsp; public Optional<Product> show(@PathVariable String id) {
&nbsp; &nbsp; &nbsp; &nbsp; return productRepository.findById(id);
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.PUT, value="/api/products/{id}")
&nbsp; &nbsp; public Product update(@PathVariable String id, @RequestBody Product product) {
&nbsp; &nbsp; &nbsp;&nbsp; &nbsp;Optional<Product> prod = productRepository.findById(id);
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdName() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdName(product.getProdName());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdDesc() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdDesc(product.getProdDesc());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdPrice() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdPrice(product.getProdPrice());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdImage() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdImage(product.getProdImage());
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.save(prod.get());
&nbsp; &nbsp; &nbsp; &nbsp; return prod.get();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.DELETE, value="/api/products/{id}")
&nbsp; &nbsp; public String delete(@PathVariable String id) {
&nbsp; &nbsp; &nbsp; &nbsp; Optional<Product> product = productRepository.findById(id);
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.delete(product.get());

&nbsp; &nbsp; &nbsp; &nbsp; return "product deleted";
&nbsp; &nbsp; }
}

For login, we need to create a POJO to mapping required fields of User. Create a new class file with the name AuthBody inside controllers package then replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

public class AuthBody {

&nbsp;&nbsp; &nbsp;private String email;
&nbsp; &nbsp; private String password;

&nbsp;&nbsp; &nbsp;public String getEmail() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEmail(String email) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.email = email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getPassword() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setPassword(String password) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.password = password;
&nbsp;&nbsp; &nbsp;}

}

Finally, create a controller for authentication with the name AuthController inside the controllers’ package. Open and edit that newly created file then replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

import static org.springframework.http.ResponseEntity.ok;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.configs.JwtTokenProvider;
import com.djamware.SecurityRest.models.User;
import com.djamware.SecurityRest.repositories.UserRepository;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;AuthenticationManager authenticationManager;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;JwtTokenProvider jwtTokenProvider;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;UserRepository users;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;private CustomUserDetailsService userService;

&nbsp;&nbsp; &nbsp;@SuppressWarnings("rawtypes")
&nbsp;&nbsp; &nbsp;@PostMapping("/login")
&nbsp;&nbsp; &nbsp;public ResponseEntity login(@RequestBody AuthBody data) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String username = data.getEmail();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, data.getPassword()));
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String token = jwtTokenProvider.createToken(username, this.users.findByEmail(username).getRoles());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Map<Object, Object> model = new HashMap<>();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("username", username);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("token", token);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ok(model);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} catch (AuthenticationException e) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new BadCredentialsException("Invalid email/password supplied");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;}

&nbsp;&nbsp; &nbsp;@SuppressWarnings("rawtypes")
&nbsp;&nbsp; &nbsp;@PostMapping("/register")
&nbsp;&nbsp; &nbsp;public ResponseEntity register(@RequestBody User user) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;User userExists = userService.findUserByEmail(user.getEmail());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (userExists != null) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new BadCredentialsException("User with username: " + user.getEmail() + " already exists");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;userService.saveUser(user);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Map<Object, Object> model = new HashMap<>();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("message", "User registered successfully");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ok(model);
&nbsp;&nbsp; &nbsp;}
}

7. Run and Test Spring Boot Security Rest using Postman

Before run and test the application, we have to populate a Role data first. Open and edit src/main/java/com/djamware/SecurityRest/SecurityRestApplication.java then add these lines of codes inside the initialization method.

@Bean
CommandLineRunner init(RoleRepository roleRepository) {

&nbsp; &nbsp; return args -> {

&nbsp; &nbsp; &nbsp; &nbsp; Role adminRole = roleRepository.findByRole("ADMIN");
&nbsp; &nbsp; &nbsp; &nbsp; if (adminRole == null) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Role newAdminRole = new Role();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newAdminRole.setRole("ADMIN");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roleRepository.save(newAdminRole);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; };

}

Next, make sure you have run the MongoDB server on your local machine then run the Gradle application using this command.

./gradlew bootRun

Or in STS just right-click the project name -> Run As -> Spring Boot App. Next, open the Postman application then change the method to GET and address to localhost:8080/api/products then click Send button.

You will see this response in the bottom panel of Postman.

{
&nbsp; &nbsp; "timestamp": "2019-03-07T13:16:34.935+0000",
&nbsp; &nbsp; "status": 401,
&nbsp; &nbsp; "error": "Unauthorized",
&nbsp; &nbsp; "message": "Unauthorized",
&nbsp; &nbsp; "path": "/api/products"
}

Next, change the method to POST then address to localhost:8080/api/auth/register then fill the body with raw data as below image then click Send button.

You will get the response in the bottom panel of Postman.

{
&nbsp; &nbsp; "message": "User registered successfully"
}

Next, change the address to localhost:8080/api/auth/login and change the body as below then click Send button.

{ "email":"[email&nbsp;protected]", "password": "q1w2we3r4" }

You will see this response in the bottom panel of Postman.

{
&nbsp; &nbsp; "username": "[email&nbsp;protected]",
&nbsp; &nbsp; "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo"
}

Copy the token then back to the GET product. Add a header with the name Authorization and the value that paste from a token that gets by login with additional Bearer prefix (with space) as below.

Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo

You should see this response after clicking the Send button.

[
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; "id": "5c80dc6cb20191520567b68a",
&nbsp; &nbsp; &nbsp; &nbsp; "prodName": "Dummy Product 1",
&nbsp; &nbsp; &nbsp; &nbsp; "prodDesc": "The Fresh Dummy Product in The world part 1",
&nbsp; &nbsp; &nbsp; &nbsp; "prodPrice": 100,
&nbsp; &nbsp; &nbsp; &nbsp; "prodImage": "https://dummyimage.com/600x400/000/fff"
&nbsp; &nbsp; }
]

You can test the POST product with the token in headers using the same way.

That it’s, the Securing RESTful API with Spring Boot, Security, and Data MongoDB tutorial. You can get the full source code from our GitHub.

Learn More

Build a Simple CRUD App with Spring Boot and Vue.js

Creating RESTful APIs with NodeJS and MongoDB Tutorial

MongoDB with Python Crash Course - Tutorial for Beginners

How to build RESTful APIs with ASP.NET Core

Understanding the basics of RESTful APIs

Developing RESTful APIs with Lumen (A PHP Micro-framework)

Java Programming Masterclass for Software Developers

Java In-Depth: Become a Complete Java Engineer!

JSP, Servlets and JDBC for Beginners: Build a Database App

JSP, Servlet, JSLT + Hibernate: A complete guide

Json Javascript database for Node.js, Electron and Browser

Json Javascript database for Node.js, Electron and Browser

JSON Javascript database for Node.js, Electron and the browser. Powered by Lodash. ⚡️

lowdb is a small local JSON database powered by Lodash (supports Node, Electron and the Browser)

Install

npm install lowdb

Alternatively, if you're using yarn

yarn add lowdb

A UMD build is also available on unpkg for testing and quick prototyping:

<script src="https://unpkg.com/[email protected]/lodash.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/low.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/LocalStorage.min.js"></script>
<script>
  var adapter = new LocalStorage('db')
  var db = low(adapter)
</script>

How to use LowDB

db.get('posts')
  .push({ id: 1, title: 'lowdb is awesome'})
  .write()
const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync')

const adapter = new FileSync('db.json')
const db = low(adapter)

// Set some defaults (required if your JSON file is empty)
db.defaults({ posts: [], user: {}, count: 0 })
  .write()

// Add a post
db.get('posts')
  .push({ id: 1, title: 'lowdb is awesome'})
  .write()

// Set a user using Lodash shorthand syntax
db.set('user.name', 'typicode')
  .write()
  
// Increment count
db.update('count', n => n + 1)
  .write()

Data is saved to db.json

{
  "posts": [
    { "id": 1, "title": "lowdb is awesome"}
  ],
  "user": {
    "name": "typicode"
  },
  "count": 1
}

You can use any of the powerful lodash functions, like _.get and _.find with shorthand syntax.

// For performance, use .value() instead of .write() if you're only reading from db
db.get('posts')
  .find({ id: 1 })
  .value()

Lowdb is perfect for CLIs, small servers, Electron apps and npm packages in general.

It supports Node, the browser and uses lodash API, so it's very simple to learn. Actually, if you know Lodash, you already know how to use lowdb

Important lowdb doesn't support Cluster and may have issues with very large JSON files (~200MB).

API

low(adapter)

Returns a lodash chain with additional properties and functions described below.

db.[...].write() and db.[...].value()

write() writes database to state.

On the other hand, value() is just _.prototype.value() and should be used to execute a chain that doesn't change database state.

db.set('user.name', 'typicode')
  .write()

Please note that db.[...].write() is syntactic sugar and equivalent to

db.set('user.name', 'typicode')
  .value()

db.write()

db._

Database lodash instance. Use it to add your own utility functions or third-party mixins like underscore-contrib or lodash-id.

db._.mixin({
  second: function(array) {
    return array[1]
  }
})

db.get('posts')
  .second()
  .value()

db.getState()

Returns database state.

db.getState() // { posts: [ ... ] }

db.setState(newState)

Replaces database state.

const newState = {}
db.setState(newState)

db.write()

Persists database using adapter.write (depending on the adapter, may return a promise).

// With lowdb/adapters/FileSync
db.write()
console.log('State has been saved')

// With lowdb/adapters/FileAsync
db.write()
  .then(() => console.log('State has been saved'))

db.read()

Reads source using storage.read option (depending on the adapter, may return a promise).

// With lowdb/FileSync
db.read()
console.log('State has been updated')

// With lowdb/FileAsync
db.read()
  .then(() => console.log('State has been updated'))

Adapters API

Please note this only applies to adapters bundled with Lowdb. Third-party adapters may have different options.

For convenience, FileSync, FileAsync and LocalBrowser accept the following options:

  • defaultValue if file doesn't exist, this value will be used to set the initial state (default: {})
  • serialize/deserialize functions used before writing and after reading (default: JSON.stringify and JSON.parse)
const adapter = new FileSync('array.yaml', {
  defaultValue: [],
  serialize: (array) => toYamlString(array),
  deserialize: (string) => fromYamlString(string)
})

Guide

How to query

With lowdb, you get access to the entire lodash API, so there are many ways to query and manipulate data. Here are a few examples to get you started.

Please note that data is returned by reference, this means that modifications to returned objects may change the database. To avoid such behaviour, you need to use .cloneDeep().

Also, the execution of methods is lazy, that is, execution is deferred until .value() or .write() is called.

Reading from existing JSON file

If you are reading from a file adapter, the path is relative to execution path (CWD) and not to your code.

my_project/
  src/
    my_example.js
  db.json 

So then you read it like this:

// file src/my_example.js
const adapter = new FileSync('db.json')

// With lowdb/FileAsync
db.read()
  .then(() => console.log('Content of my_project/db.json is loaded'))

Examples

Check if posts exists.

db.has('posts')
  .value()

Set posts.

db.set('posts', [])
  .write()

Sort the top five posts.

db.get('posts')
  .filter({published: true})
  .sortBy('views')
  .take(5)
  .value()

Get post titles.

db.get('posts')
  .map('title')
  .value()

Get the number of posts.

db.get('posts')
  .size()
  .value()

Get the title of first post using a path.

db.get('posts[0].title')
  .value()

Update a post.

db.get('posts')
  .find({ title: 'low!' })
  .assign({ title: 'hi!'})
  .write()

Remove posts.

db.get('posts')
  .remove({ title: 'low!' })
  .write()

Remove a property.

db.unset('user.name')
  .write()

Make a deep clone of posts.

db.get('posts')
  .cloneDeep()
  .value()

How to use id based resources

Being able to get data using an id can be quite useful, particularly in servers. To add id-based resources support to lowdb, you have 2 options.

shortid is more minimalist and returns a unique id that you can use when creating resources.

const shortid = require('shortid')

const postId = db
  .get('posts')
  .push({ id: shortid.generate(), title: 'low!' })
  .write()
  .id

const post = db
  .get('posts')
  .find({ id: postId })
  .value()

lodash-id provides a set of helpers for creating and manipulating id-based resources.

const lodashId = require('lodash-id')
const FileSync = require('lowdb/adapters/FileSync')

const adapter = new FileSync('db.json')
const db = low(adapter)

db._.mixin(lodashId)

// We need to set some default values, if the collection does not exist yet
// We also can store our collection
const collection = db
  .defaults({ posts: [] })
  .get('posts')

// Insert a new post...
const newPost = collection
  .insert({ title: 'low!' })
  .write()

// ...and retrieve it using its id
const post = collection
  .getById(newPost.id)
  .value()

How to create custom adapters

low() accepts custom Adapter, so you can virtually save your data to any storage using any format.

class MyStorage {
  constructor() {
    // ...
  }

  read() {
    // Should return data (object or array) or a Promise
  }

  write(data) {
    // Should return nothing or a Promise
  }
}

const adapter = new MyStorage(args)
const db = low(adapter)

See src/adapters for examples.

How to encrypt data

FileSync, FileAsync and LocalStorage accept custom serialize and deserialize functions. You can use them to add encryption logic.

const adapter = new FileSync('db.json', {
  serialize: (data) => encrypt(JSON.stringify(data)),
  deserialize: (data) => JSON.parse(decrypt(data))
})

Changelog

See changes for each version in the release notes.

Limits

Lowdb is a convenient method for storing data without setting up a database server. It is fast enough and safe to be used as an embedded database.

However, if you seek high performance and scalability more than simplicity, you should probably stick to traditional databases like MongoDB.

Source Code

https://github.com/typicode/lowdb

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”.