Why you should use a Go backend in Flutter – LogRocket Blog


Flutter is a popular open source framework for creating cross-platform applications for release on Android, iOS, Linux, macOS, Windows, Fuchsia, web, and others. Flutter is growing more popular every day because of its performance and inbuilt platform-independent UI widgets.

Go is a compiled, statically typed, high-performance language with a simple syntax.

Flutter and Go became popular in the developer community after Google initiated both open source projects. Nowadays, many developers choose to use Go and Flutter together. Using a Go backend for a Flutter frontend specifically brings many advantages.

In this article, we’ll discuss these advantages and verify them practically by building a full-stack application.

Advantages of using a Go backend for Flutter apps

Application frontends usually communicate with the server-side modules with various network communication concepts, such as REST, WebSocket, SOAP, and gRPC.

The above communication concepts are tech stack-agnostic, so the backend technology doesn’t affect the frontend and vice versa. However, Go-based backends bring numerous nontechnical and hidden benefits for Flutter frontends. Further, you can avoid time-consuming business logic rewrites by directly using Go modules in the Flutter app.

Both Go and Flutter are Google open source projects. The Google open source community backs both projects by offering free community-based developer support, contributing code, and creating resources. You can discuss your Go development problems in the official Go mailing thread and Flutter-related problems in the official Flutter mailing list.

Google released Go v1 in 2012 and introduced Flutter v1 in 2018, but both technologies experienced rapid growth for cloud-based business applications in late 2019. Both projects are now growing in popularity every day and come with an excellent reputation, community support, and up-to-date, Google-engineered technology.

Go and Flutter are performance-first technologies

Nowadays, some developers tend to overlook application performance due to powerful computer hardware components. For example, many individuals use powerful computers and mobile devices, so hybrid apps don’t show performance issues despite those apps generally performing slower than native apps. Additionally, many web developers rarely need to optimize web backend performance due to strong cloud computing infrastructure.

Both Go and Flutter projects strive to solve the primary technical problem by carefully considering performance factors.

Flutter offers near-native performance with a rendering canvas powered by Skia and the native platform channels concept.

The Go compiler produces fast and optimized native binaries and makes Go quick and agile, similar to other modern, popular, enterprise-level programming languages like C#, Java, and JavaScript (Node.js).

A Go backend offers fast and efficient native server-side services for Flutter applications to achieve better native performance.

Similarities in development environment

Flutter uses Dart as the cross-platform application development language. Dart and Go offer features to solve different technical problems. However, Go/Dart syntax, developer tools, and third-party libraries have considerable similarities. Therefore, the same full-stack development team can work on both backend and frontend projects without any productivity issues. Flutter developers can also get started with Go backend development comfortably thanks to Go’s minimal syntax.

Moreover, Go development tools work perfectly on all Flutter development tools’ operating systems. As a result, you can configure a productive Go development environment on your Flutter development computer.

Reusing Go backend logic in Flutter

Sometimes we have to reuse backend code directly in a frontend application. If you use Node.js for the backend and React Native for the frontend, you can easily share common business logic by creating a JavaScript package.

However, Dart’s server-side support is still growing and is not comparable with the Go ecosystem yet, so you need to think twice before using Dart for writing the backend.

If you use C#, Java, or Node.js for developing your backend, you may have to rewrite the same existing business logic in Dart on the Flutter frontend. The Go mobile project offers a way to call Go code from the platform-specific mobile development environments, i.e., Java and Android.

Therefore, we can connect Go mobile with Flutter and build an efficient way to reuse Go-based business logic.

Using Go for serving the Flutter web app

As you may already know, users can access the Flutter app from a web browser with Flutter web support. But how can you serve your Flutter web app from your cloud environment? You either need to use a prebuilt static server or write one with a preferred backend language.

It’s possible to write a fast and complete static file server in Go with a few lines of code. You can even serve your Flutter web app from the Go RESTful backend without many configurations.

Tutorial: Building a Flutter app with a Go backend

Now that we know the benefits of using a Go backend for a Flutter application , let’s develop a Go RESTful web service and a Flutter frontend to verify the above advantages. We are going to build a full-stack product list application with a Go REST API and Flutter frontend. The Go-based RESTful web service will return a product list in JSON format and the Flutter app will display a product list by calling the web service.

We will also convert the Flutter app into a web app and serve it using the same RESTful web service. Finally, I will demonstrate how to share Go code with the Flutter app by using the Go mobile project.

Developing a Go RESTful backend

We are going to create a RESTful backend to produce a JSON-formatted product list. First, install the latest Go development tools if your computer doesn’t have the Go compiler. Next, create a new Go module with the following commands to get started:

mkdir go_backend
cd go_backend
go mod init go_backend

We need an HTTP routing library for developing RESTful web services. The Gin web framework offers almost all HTTP-based backend development features, such as routing, JSON binding, and validation. Add the Gin framework package to the current project with the following command:

go get -u github.com/gin-gonic/gin

We also need to enable CORS since we are going to use Flutter web in this tutorial. Download the Gin CORS middleware package into your project with the following command:

go get github.com/gin-contrib/cors

Now, create a file named main.go and add the following code:

package main
import (
    "github.com/gin-gonic/gin"
    "github.com/gin-contrib/cors"
)
type Product struct 
    Id int `json:"id"`
    Name string `json:"name"`
    Price int `json:"price"`
    Description string `json:"description"`

func productsHandler(c *gin.Context) 
    products := []Product 
        Product 100, "BassTune Headset 2.0", 200, "A headphone with a inbuilt high-quality microphone",
        Product 101, "Fastlane Toy Car", 100, "A toy car that comes with a free HD camera",
        Product 101, "ATV Gear Mouse", 75, "A high-quality mouse for office work and gaming",
    
    c.JSON(200, gin.H
        "products": products,
    )

func main() 
    r := gin.Default()
    r.Use(cors.Default())
    r.GET("/products", productsHandler)
    r.Run(":5000")

The above code implements the GET /products endpoint for returning a JSON-formatted product list. Here we construct a static product list by creating a Product struct slice with the [] Product syntax. We use a hardcoded product list for demonstration purposes, but you can use any preferred database connection to fetch stored product details.

Testing the Go backend

Let’s test the above Go backend. First, start the web service with the following command:

go run main.go

The above command starts the Gin RESTful server for accepting HTTP requests from the port 5000. You can test the product list endpoint with the well-known Postman tool, as shown below.

Creating the application frontend with Flutter

Let’s create a product list with Flutter and display data from the above Go backend. If you haven’t already installed Flutter, you can easily do so from the official Flutter binary releases page.

First, create a new Flutter application with the following command:

flutter create flutter_frontend

Once the project is created, run it with the flutter run command and test it on Chrome or your mobile device to verify that everything works as expected. We need to create Dart classes for each primary JSON object to make the codebase maintainable and readable. Add the following code to the lib/product_model.dart file to define the product model:

class Product 
  final int id;
  final String name;
  final String description;
  final int price;

  const Product(
    required this.id,
    required this.name,
    required this.description,
    required this.price,
  );

  factory Product.fromJson(Map<String, dynamic> json) 
    return Product(
      id: json['id'],
      name: json['name'],
      description: json['description'],
      price: json['price']
    );
  

Next, we can create a Dart service to communicate with the Go backend. We will use the Dio HTTP client library, so add it to your Flutter project with the following command:

flutter pub add dio

Now, create the product service implementation in the lib/product_service.dart file with the following Dart source:

import 'package:dio/dio.dart';
import 'package:flutter_frontend/product_model.dart';

class ProductService 
  final String productsURL = 'http://localhost:5000/products';
  final Dio dio = Dio();

  ProductService();

  Future<List<Product>> getProducts() async 
    late List<Product> products;
    try 
      final res = await dio.get(productsURL);

      products = res.data['products']
        .map<Product>(
          (item) => Product.fromJson(item),
        )
        .toList();
    
    on DioError catch(e) 
      products = [];
    

    return products;
  

Here, we created the getProducts asynchronous function to get products as instances of the Product model by calling the Go backend via the Dio client. The above source code offers business data via a Dart service, but if you work with many HTTP endpoints you can use the repository pattern to organize the code better.

Finally, we can create the product list application frontend by importing the above product service. Replace the existing code in the lib/main.dart file with the following code:

import 'package:flutter/material.dart';
import 'package:flutter_frontend/product_service.dart';
import 'package:flutter_frontend/product_model.dart';

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

class MyApp extends StatelessWidget {
  final _productService = ProductService();

  @override
  Widget build(BuildContext context) 
    const title="Product List";

    return MaterialApp(
      title: title,
      theme: new ThemeData(scaffoldBackgroundColor: const Color(0xffdddddd)),
      home: Scaffold(
        appBar: AppBar(
          title: const Text(title),
        ),
        body: FutureBuilder<List<Product>>(
          future: _productService.getProducts(),
          builder: (context, snapshot) 
            var products = snapshot.data ?? [];

            if(!snapshot.hasData) 
              return const Center(child: CircularProgressIndicator());
            

            return ListView.builder(
              itemCount: products.length,
              itemBuilder: (context, index) 
                var product = products[index];
                return ListTile(
                  title: Text(products[index].name),
                  subtitle: Text('#$product.id $product.description'),
                  trailing: Text('$$product.price')
                );
              ,
            );
          ,
        ),
      ),
    );
  
}

In the above code snippet, we used the FutureBuilder class to display a loading animation until the frontend fetches all products from the backend. Run the application on your mobile device or Chrome by entering the flutter run command. Make sure to replace localhost with your computer’s local IP address if you are running the app on a Flutter device other than Chrome.

You will see the working product list application interface, as shown below.

Our product list application is running in Chrome

Serving the Flutter web app with Go

Now we are going to expose our Flutter application as a web app via the Go backend . Then, we can access the app with any modern web browser. We can easily add static file serving support to the existing web service via the Gin static middleware. Install the static middleware package from the project directory.

go get github.com/gin-contrib/static

Next, add the following package import to the main Go source file.

"github.com/gin-contrib/static"

Finally, ask the Gin framework to serve static web content with the following code line:

r.Use(static.Serve("https://blog.logrocket.com/", static.LocalFile("./static", false)))

Make sure that the final web service source looks like this:

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/gin-contrib/cors"
    "github.com/gin-contrib/static"
)

type Product struct 
    Id int `json:"id"`
    Name string `json:"name"`
    Price int `json:"price"`
    Description string `json:"description"`


func productsHandler(c *gin.Context) 
    products := []Product 
        Product 100, "BassTune Headset 2.0", 200, "A headphone with a inbuilt high-quality microphone",
        Product 101, "Fastlane Toy Car", 100, "A toy car that comes with a free HD camera",
        Product 101, "ATV Gear Mouse", 75, "A high-quality mouse for office work and gaming",
    
    c.JSON(200, gin.H
        "products": products,
    )


func main() 
    r := gin.Default()
    r.Use(cors.Default())
    r.Use(static.Serve("https://blog.logrocket.com/", static.LocalFile("./static", false)))

    r.GET("/products", productsHandler)

    r.Run(":5000")

We can now build the Flutter web app to get static web resources. Enter the following command and generate Flutter web app resources:

flutter build web

Create a new directory named static inside the Golang project and copy all generated Flutter web app resources from the ./build/web directory to the static directory.

Start the Go backend server and go to the http://localhost:5000 URL from your web browser. You will see the working Flutter web application, as shown below.

Our product list in Flutter

You can deploy the Go project into your cloud environment with Flutter web resources by using a container system like Docker. Then everyone can access your full-stack Flutter web app from web browsers.

Reusing Go code in the Flutter app

The Go mobile project offers tools to generate native Android and iOS libraries from Go source files. The Flutter project uses platform-specific host applications, aka embedders, to initialize the Flutter engine on each platform. Therefore, we can use Go modules in Flutter with the Go mobile project and Flutter platform channel APIs. This approach is helpful for developers who want to reuse Go backend code within the Flutter app without rewriting Go modules in Dart.

Now, we are going to modify the well-known Flutter demo application by displaying a random number   instead of incrementing the existing number with each tap on the floating action button. We will generate this random number via a Go module. In the following example, I will explain how to embed a Go module in an Android app. You can use a similar approach to embed Go modules in iOS apps, too.

Before you continue with the tutorial, make sure that your computer has the following components, which can be easily installed via Android Studio:

  • Android SDK
  • Android NDK
  • Clang compiler and Make

First, we need to install the Go mobile CLI with the following command:

go install golang.org/x/mobile/cmd/[email protected]
gomobile init

If the gomobile command doesn’t work after the installation process ,  you can solve the issue by adding the Go mobile binary to the PATH environment variable, as shown below.

export PATH=$PATH:~/go/bin

Let’s create a new Go module to generate a random number. First, create a new Go project in your working directory.

mkdir gomobilelib
cd gomobilelib
go mod init gomobilelib

Next, create a new file named gomobilelib.go and add the following source code.

package gomobilelib

import "math/rand"

type (
    GoMobileLib struct 
)

func (p *GoMobileLib) RandomNumber() int 
    return rand.Intn(100);

We can generate native platform-specific libraries for each mobile operating system with Go mobile’s binding feature. Before using it, we need to install the Go mobile bind package.

go get golang.org/x/mobile/bind

Now we can generate an Android library file using the following command:

gomobile bind --target android

After running the above command, you can see the gomobilelib.aar file in the Go module directory. The .aar file contains the Go runtime libraries and the above gomobilelib module in platform-specific binary format for each mobile CPU architecture.

Let’s use the Android library by creating a new Flutter project. Create a new Flutter project with a Java-based host app.

flutter create gomobilefrontend -a java

Copy the gomobilelib.aar file to the ./gomobilefrontend/android/app/src/main/libs directory. Link the newly created library with the Android host app by adding the following configuration to the ./gomobilefrontend/android/app/build.gradle file.

repositories 
    flatDir
         dirs 'src/main/libs'
    

dependencies 
    api(name:'gomobilelib', ext:'aar')

Next, replace the existing code in the MainActivity.java file with the following code:

package com.example.gomobilefrontend;

import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import gomobilelib.GoMobileLib;

public class MainActivity extends FlutterActivity 
  private static final String CHANNEL = "example.com/gomobilelib";

  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) 

    GoMobileLib goMobileLib = new GoMobileLib();

    super.configureFlutterEngine(flutterEngine);
      new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
        .setMethodCallHandler(
          (call, result) -> 
            if(call.method.equals("getRandomNumber")) 
              result.success(goMobileLib.randomNumber());
            
            else 
              result.notImplemented();
            
          
        );
  

The above code exposes the Android library’s randomNumber function as getRandomNumber to the Flutter app via the platform channels API. Now we can invoke getRandomNumber from the Flutter app to receive a new random number.

You can now create an asynchronous Dart function to call the exported Android library function. For example, the following Dart function updates the _counter variable with the random number generated by the Go module:

static const platform = MethodChannel('example.com/gomobilelib');
int _counter = 0;

Future<void> _getRandomNumber() async 
  int randomNumber;
  try 
    randomNumber = await platform.invokeMethod('getRandomNumber');
   on PlatformException catch (e) 
    randomNumber = 0;
  

  setState(() 
    _counter = randomNumber;
  );

Note that we need to use the same platform channel identifier in the Flutter app and Android host app to get everything working properly. Look at the following complete source code of the modified demo application that displays random numbers:

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

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


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

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'GoMobileFlutter',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter with Go Mobile'),
    );
  


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

  final String title;

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


class _MyHomePageState extends State<MyHomePage> 
  static const platform = MethodChannel('example.com/gomobilelib');
  int _counter = 0;

  Future<void> _getRandomNumber() async 
    int randomNumber;
    try 
      randomNumber = await platform.invokeMethod('getRandomNumber');
     on PlatformException catch (e) 
      randomNumber = 0;
    

    setState(() 
      _counter = randomNumber;
    );
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'The following number was generated by Go:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _getRandomNumber,
        tooltip: 'Get a random number',
        child: const Icon(Icons.add),
      ),
    );
  

If you run the Android application with the above source code using the flutter run command, you can generate a new random number by clicking on the floating action button, as shown in the following preview.

Our Go-powered Flutter app is now running in Android

Similar to the above example application, you can reuse your Go modules in your Flutter applications without rewriting them in Dart. Therefore, if you select Go for writing backend web services, you can productively use the core business logic modules directly with the Flutter frontend. Learn more about platforms channels from the official Flutter documentation.

This project’s source code is available at my GitHub repository.

Conclusion

In this article, we discussed the advantages of using Go for Flutter applications with a sample RESTful web service. Many developers use the RESTful web service pattern for modern cloud-oriented applications, but the RESTful approach doesn’t solve every technical requirement. For bidirectional asynchronous communications, for example, you may have to use the WebSockets protocol; for lightweight and high-speed streaming calls, you can consider using the gRPC protocol.

The Go package ecosystem provides packages for both WebSockets and gRPC protocols, so you can use Go to implement backends for your Flutter frontends with any preferred communication protocol.

As we discussed, Go backends bring many benefits for Flutter apps. There are many similarities in Go and Flutter development environments, and both technologies have the same popularity level and reputation, developer support, and growing package ecosystem.



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

The 10 best websites for free music samples in 2022

Next Article

No brakes! Will the EVs of the future omit friction pads entirely?

Related Posts