gRPC Flutter Client Integration

Salih Can
7 min readJul 1, 2023

--

Hello! In previous articles, we covered an introduction to the gRPC framework and how to build a gRPC server using TypeScript. In this article, I will walk you through the process of developing a client application using the gRPC framework in Flutter, step by step.

Additionally, we will learn how to use the service we wrote on the server side, using the proto file we used in our previous articles, in our Flutter gRPC client application.

Let’s get started with creating our Flutter gRPC client application!

Firstly, you can get more detailed information about gRPC and see how to create the server side using TypeScript from the following links.

As usual, let’s continue, and I’m providing the visual representation of the structure we will achieve at the end of the article below. Additionally, if you want to see the final version of the project on GitHub, I have included the GitHub link at the end of the article.

We used the following proto model on the server side. On the client side, we will use the same model to generate the necessary buffer files. This way, we will be able to execute the method on the server from the client side (RPC).

path:lib/app/data/proto

After adding the above file to the ./lib/app/data/proto directory, let’s install the package that will generate Dart classes. You can run the following line through the terminal to install protoc in one line.

$ dart pub global activate protoc_plugin

Let’s create a shell script named proto_generator.sh in the ./scripts directory and add the following line to it. (I should also mention that you can start the code generation from the protoc onwards without creating the shell script below.)

cd .. &&  protoc --dart_out=grpc:lib/app/data/generated/ --proto_path=lib/app/data/proto lib/app/data/proto/*.proto

By invoking the protoc shell command in the above lines with the provided parameters, it will generate Dart classes that correspond to our proto files. Below, I am including an explanation of the parameters used.

  • protoc: Calls the shell script we created.
  • dart_out: Specifies where the generated Dart classes should be produced.
  • proto_path: Specifies the location of our proto files. Although this parameter is optional, not providing it can result in a complex file structure since it generates the files using a method like $currentProtoPath/generatedFiles. Therefore, using this parameter is important.
  • The last path we provided indicates the location of the actual proto files. This field is required, and no prefix is needed. In this usage, we indicated that all files ending with “.proto” inside the proto file are proto files.

We will run the shell script proto_generator.sh that we created in the terminal using the following method to generate Dart classes based on our proto file.

sh proto_generator.sh

When you successfully complete these steps, your file structure should look like the one in the photo below.

When you glance through these generated files, you will notice that their content is quite complex. However, the content itself is not important for us. I have mentioned the reasons behind it multiple times in the previous articles. I provided the links at the beginning of this article, so if you still have any questions regarding the reasons, you can take a look.

Now it’s time to create the RPC client that we can use to invoke server methods and establish a server connection.

To create the RPC client, I have created an RPC Client class and customized it according to my own client by extending the class that handles network operations in the gRPC Dart library.

I have created the greeter_service_handler.dart file in the lib/app/data/remote directory and filled its contents as follows.

import 'dart:io';
import 'package:grpc/grpc.dart';import '../generated/greeter.pbgrpc.dart';class GreeterServiceHandler extends GreeterClient {
GreeterServiceHandler()
: super(
ClientChannel(
_host,
port: 1337,
options: const ChannelOptions(
credentials: ChannelCredentials.insecure(),
),
),
options: CallOptions(
timeout: const Duration(seconds: 5),
),
);
static String get _host {
if (Platform.isAndroid) {
return '10.0.2.2';
} else if (Platform.isIOS) {
return '0.0.0.0';
}
return 'localhost';
}
}

Above, I specified the port as 1337 because in the previous article, we set the port to 1337 for the server to use. Additionally, I dynamically handled the host address because on Android devices, local loopback access is done using 10.0.2.2.

Below, you can see the final file structure of the project

In this way, we have completed the logical operations of the gRPC client in Dart. Now, it’s time to create the Flutter application to call the server methods through the GreeterServiceHandler we created and display the responses on the screen.

Our sample application will consist of a single screen. When we created the server side, we were appending ‘Hello ‘ to the String value in the request coming to the SayHello method and returning it as the response. Similarly, when the application is launched, we will send the ‘gRPC’ message and display the response on the screen.

In the first step, we will create the application using ‘MaterialApp,’ which will set up the Material skeleton of our application.

Before proceeding with these steps, let’s not forget to run the server!

I have created the main.dart file in the lib/main.dart directory and added the following lines to its content.

import 'package:flutter/material.dart';
import 'app/init/grpc_client_flutter_app.dart';void main() {
runApp(const ClientAppForGRPC());
}

Then, I created the ClientAppForGRPC class in the lib/app/init directory with the name /grpc_client_flutter_app.dart and added the following lines to its content.

import 'package:flutter/material.dart';
import 'package:grpc_client_flutter/app/presetantion/home/home_view.dart';
class ClientAppForGRPC extends StatelessWidget {
const ClientAppForGRPC({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomeView(),
);
}
}

As you can see above, after naming our initial screen of the application as HomeView, let’s create the HomeView.

In order to keep the progress of this project as simple as possible, I have chosen not to use any state management package. Instead, I separated the logical code from the UI by inheriting the relevant state. (If you have any questions about this, feel free to ask in response to this article or reach out to me on other platforms.)

I have created the HomeView and HomeViewModel classes in the lib/app/presentation/home directory. The file names are home_view.dart and home_view_model.dart, respectively.

Here is the content of the HomeView class.

import 'package:flutter/material.dart';
import 'home_view_model.dart';class HomeView extends StatefulWidget {
const HomeView({Key? key}) : super(key: key);
@override
State<HomeView> createState() => _HomeView();
}
class _HomeView extends HomeViewModel {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(screenTitle),
),
);
}
}

As you can see, there isn’t much detail to be explained in this class. However, to briefly mention, this screen is a simple screen that displays text in the middle, and it gets the text value screenTitle from the HomeViewModel class.

On the client side, RPC operations are handled mostly in the HomeViewModel class, which I will create with the following lines. I will write its description below. I have created the HomeViewModel class in the lib/app/presentation/home directory with the file name home_view_model.dart, as mentioned earlier.

import 'package:flutter/material.dart';
import '../../data/generated/greeter.pbgrpc.dart';
import '../../data/remote/greeter_service_handler.dart';
import 'home_view.dart';
abstract class HomeViewModel extends State<HomeView> {
String screenTitle = 'loading...';
@override
void initState() {
_init();
super.initState();
}
Future<void> _init() async {
final greeterService = GreeterServiceHandler();
final request = HelloRequest()..name = 'gRPC'; final response = await greeterService.sayHello(request); await Future.delayed(const Duration(seconds: 3)); setState(() {
screenTitle = response.message;
});
}
}

Inside the class, a variable called screenTitle is defined and initialized with the valueloading…. When the HomeView screen starts, the HomeViewModel state is created by the Stateful Widget.

Upon creation, the _init method is called within the initState. This method first creates an instance of the GreeterServiceHandler class. Then, it assigns the value ‘gRPC’ to the name property within the HelloRequest class.

Using the instance of the GreeterServiceHandler class we created earlier, we invoke the method called sayHello and provide the HelloRequest class as a parameter. By calling this method, we execute the SayHello method on the server side using a simple logic. After a certain period of time, we receive the response from the server and assign it to a variable called response.

After receiving the response, we update the screenTitle variable with the response, and we can see the result on the screen altogether.

Following the response from the server, I added a 3-second delay. The reason for this is that due to the local environment and network latency, it may take milliseconds to receive the response, and we would not be able to see theloading… text at all. With this delay, we now first see loading… and then we see the Hello gRPC message on the screen.

The left console represents the server-side, while the right console represents the client-side.

In this article, we learned how to create a client application using gRPC in Flutter. We automatically generated our code using protobuf files and established communication between the client and server applications through gRPC services.

I hope this article has been a useful resource for those interested in gRPC. I tried to explain the concepts as clearly and simply as possible. However, if you have any questions or concerns, please feel free to reach out to me through comments on the article or platforms like LinkedIn.

Result Project:

--

--

Salih Can
Salih Can

No responses yet