repo: Goro-o56/grpc-flutter-client (github.com)
まずはローカルで通信する
FlutterでProjectを作る。 (↓完成したもの)
protoを書く
//example.proto
syntax = "proto3";
package example;
service StringService {
rpc ProcessStrings (StringArray) returns (StringResult);
}
message StringArray {
repeated string values = 1;
}
message StringResult {
string value = 1;
}
Flutterのクライアントを作る。
pubspec.yamlにgrpcを追加
コードを生成。
protoc --dart_out=grpc:lib/pkg/ -Iprotos protos/example.proto
example.dartからexportする(参照しやすくする。)
library example;
export 'pkg/example.pb.dart';
export 'pkg/example.pbenum.dart';
export 'pkg/example.pbgrpc.dart';
export 'pkg/example.pbjson.dart';
インターフェースが出来たので、それに依存する形でコードを書く。
import 'package:flutter/material.dart';
import 'package:grpc_flutter_golang/example.dart';
import 'package:grpc/grpc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter gRPC Client',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter gRPC Client'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _responseText = '';
Future<void> _callGrpcService() async {
final channel = ClientChannel(
'localhost',
port: 8080,
options: const ChannelOptions(credentials: ChannelCredentials.insecure()),
);
final client = StringServiceClient(channel);
try {
final response = await client.processStrings(
StringArray(values: ['Hello', 'World']),
);
setState(() {
_responseText = 'Response from server: ${response.value}';
});
} catch (e) {
print('Caught error: $e');
}
await channel.shutdown();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$_responseText',
style: Theme.of(context).textTheme.headline6,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _callGrpcService,
tooltip: 'Call gRPC Service',
child: Icon(Icons.send),
),
);
}
}
テストで起動してみて、大丈夫だったので進む。
Goでサーバーを作る。
repo: Goro-o56/go-grpcsrv (github.com)
ここからgRPCを使えるようにする。
https://grpc.io/docs/languages/go/quickstart/
go-grpcsrvプロジェクトを作る。(↓完成したもの)
コードを生成する。先ほどと同様に
/protosで次のコマンドを実行
protoc --go_out=./pkg/grpc --go-grpc_out=./pkg/grpc example.proto
生成されたpkgをgo-grpcsrb配下に置く。
インターフェースに依存する形でコードを書く
package main
import (
"context"
"fmt"
examplepb "go-grpcsrv/pkg/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"log"
"net"
"os"
"os/signal"
"strings"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
listener, err := net.Listen("tcp", fmt.Sprintf(":%s", port))
if err != nil {
panic(err)
}
s := grpc.NewServer()
examplepb.RegisterStringServiceServer(s, NewServer())
reflection.Register(s) //grpcurl用にリフレクションする。
go func() {
log.Printf("start gRPC server port: %v", port)
err := s.Serve(listener)
if err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
log.Println("stopping gRPC server...")
s.GracefulStop()
}
func NewServer() *StringServiceServer {
//これはコンストラクタ。呼び出すことで実体が返される
return &StringServiceServer{}
}
type StringServiceServer struct {
examplepb.UnimplementedStringServiceServer
}
func (s *StringServiceServer) StringProcess(ctx context.Context, strArr *examplepb.StringArray) (*examplepb.StringResult, error) {
str := strings.Join(strArr.Values, "")
return &examplepb.StringResult{Value: str}, nil
}
コードはこの方のを参考にしました。
サーバーを起動し、grpcurlを送る(この際、.protoの存在するディレクトリからコマンドを叩く事。)
grpcurl -plaintext -d '{"values": ["aaa", "bbb"]}' -proto example.proto localhost:8080 example.StringService/ProcessStrings
Flutterでリクエストを送信してこの画面が出る事を確認。