How We Built a Cross‑Platform Flutter App with gRPC and gRPC‑Gateway
This article details the technical decisions and implementation steps for a Flutter‑based mobile product that uses gRPC for high‑performance RPC, gRPC‑Web for browser compatibility, and gRPC‑Gateway to expose RESTful APIs, covering code generation, library design, debugging tricks, and future roadmap.
Background
To improve code reuse and reduce development cost on mobile platforms, the team chose Flutter as the cross‑platform framework and gRPC as the RPC solution because it offers high performance, language‑agnostic interfaces, and seamless client‑server interaction.
gRPC Overview
gRPC is an open‑source RPC framework built on HTTP/2 and Protocol Buffers. It generates client and server stubs from service definitions, allowing clients to call remote methods as if they were local functions.
Key Features
IDL‑driven code generation using ProtoBuf, supporting many languages.
HTTP/2 based design with multiplexing, header compression, and bidirectional streaming.
Efficient binary serialization, though debugging can be harder than JSON.
Gateway support to expose RESTful endpoints for front‑end friendliness.
Extensible ProtoBuf plugins for custom annotations.
gRPC‑Web
gRPC‑Web provides a JavaScript library for browsers to call gRPC services via an Envoy proxy. Because it adds deployment complexity, the project used gRPC‑Gateway to expose RESTful APIs instead of direct gRPC‑Web calls.
gRPC‑Gateway
grpc‑gateway is a protoc plugin that reads gRPC service definitions and generates a reverse‑proxy server, translating JSON REST requests into gRPC calls. This simplifies front‑end integration and testing with tools like Postman.
GET /index HTTP/1.1
grpc-metadata-platform: ios
grpc-metadata-device_id: xxxxxxx
grpc-metadata-timestamp: 1562641496
grpc-metadata-locale: en_US
grpc-metadata-version: 1.0.0
Host: gateway.hostname.comBase Libraries
Dart
A BaseClient library manages connection pooling and metadata injection for the Flutter client.
var base = BaseClient(host: 'rpc.hostname.com', port: 443, secure: true);
final md = await base.metadata;
final stub = AuthClient(base.channel, options: CallOptions(metadata: md));Golang
The backend services run both gRPC and gateway endpoints. An interceptor chain adds metrics, logging, authentication, and validation.
svrMux := &ServerMux{ServeMux: http.NewServeMux()}
svrMux.svr = grpc.NewServer(
grpc.UnaryInterceptor(
middleware.ChainUnaryServer(
recovery.UnaryServerInterceptor(...),
prometheus.UnaryServerInterceptor,
svrMux.UnaryResponseInterceptor(),
proto.UnaryServerInterceptor(),
log.UnaryServerInterceptor(conf.GlobalCallback()),
auth.UnaryServerInterceptor(conf.GlobalCallback()),
validator.UnaryServerInterceptor(),
),
),
grpc.StreamInterceptor(...),
)
svrMux.mux = runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, json.Proto),
runtime.WithProtoErrorHandler(svrMux.protoErrorHandler),
runtime.WithStreamErrorHandler(svrMux.streamErrorHandler),
)
svrMux.ServeMux.Handle("/", svrMux.mux)Component registration follows a Component interface:
type Component interface {
Name() string
InitStorage() error
InitGRPC(svc Service) error
InitGateway(svc Service) error
StorageCron()
}Components are registered and the server started with:
base.DefaultServer.RegisterComponent(&user.Component{})
base.DefaultServer.RegisterComponent(&push.Component{})
base.DefaultServer.Serve()Proto Specification
All service definitions and messages are placed in .proto files with markdown‑compatible comments, allowing automatic generation of documentation and code.
message ExampleMessage {
uint64 id = 1; // concise field comment
}
service Example {
rpc test (ExampleMessage) returns (ExampleMessage) {
option (auth.access) = { level: LOW_ACCESS_LEVEL };
option (google.api.http) = { post: "/example/test" body: "*" };
}
}Code Generation
For Go, the gengo target runs protoc with plugins to generate Go stubs, gateway code, validation, and authentication helpers.
@protoc -Iproto \
-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
-I${GOPATH}/src/github.com/lnnujxxy/protoc-gen-validate \
-I${GOPATH}/src/github.com/youlu-cn/grpc-gen/protoc-gen-auth \
--go_out=plugins=grpc:go/pb \
--grpc-gateway_out=logtostderr=true:go/pb \
--validate_out="lang=go:go/pb" \
--auth_out="lang=go:go/pb" \
proto/*.protoFor Dart, a similar make target generates Dart protobuf files and gRPC stubs.
@protoc --dart_out=dart/front_user/lib/src/generated $(PROTO_ROOT_DIR)/include/google/protobuf/*.proto
@protoc -Iproto \
-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
-I${GOPATH}/src/github.com/lnnujxxy/protoc-gen-validate \
-I${GOPATH}/src/github.com/youlu-cn/grpc-gen/protoc-gen-auth \
--dart_out=grpc:dart/user/lib proto/*.protoDocumentation Generation
A custom protoc-gen-markdown plugin creates markdown API docs from proto files, including both gRPC and RESTful descriptions and JSON examples.
@protoc -Iproto \
-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
-I${GOPATH}/src/github.com/lnnujxxy/protoc-gen-validate \
-I${GOPATH}/src/github.com/youlu-cn/grpc-gen/protoc-gen-auth \
--markdown_out=":doc" proto/*.protoDebugging
Because gRPC uses binary protobuf over HTTP/2, traditional packet capture is difficult. The team injected logging middleware to capture request and response metadata, providing a pseudo‑packet‑capture view for troubleshooting.
Future Plans
Implement gRPC streaming support.
Integrate full‑trace capabilities across the framework.
Iterate on the framework to provide scaffolding that simplifies creation of new components and services.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Huajiao Technology
The Huajiao Technology channel shares the latest Huajiao app tech on an irregular basis, offering a learning and exchange platform for tech enthusiasts.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
