Skip to content

Dart Language Support

Status: ✅ Full Support
Example: examples/dart-example/

Dart support provides complete Protocol Buffer and gRPC integration for Flutter and server applications.

PluginDescriptionGenerated Files
protoc-gen-dartBase messages & gRPC*.pb.dart, *.pbgrpc.dart, *.pbenum.dart, *.pbjson.dart
languages.dart = {
enable = true;
outputPath = "lib/proto";
};
languages.dart = {
enable = true;
outputPath = "lib/proto";
packageName = "my_app_proto";
options = [
"generate_kythe_info" # IDE support metadata
];
grpc = {
enable = true;
options = [
"generate_metadata"
];
};
};
proto/example/v1/example.proto
syntax = "proto3";
package example.v1;
message ExampleMessage {
int32 id = 1;
string name = 2;
string email = 3;
repeated string tags = 4;
optional string description = 5;
TimestampMessage created_at = 6;
}
message TimestampMessage {
int64 seconds = 1;
int32 nanos = 2;
}
service ExampleService {
rpc CreateExample(CreateExampleRequest) returns (CreateExampleResponse);
rpc GetExample(GetExampleRequest) returns (GetExampleResponse);
rpc ListExamples(ListExamplesRequest) returns (ListExamplesResponse);
rpc WatchExamples(ListExamplesRequest) returns (stream ExampleMessage);
}
message CreateExampleRequest {
ExampleMessage example = 1;
}
message CreateExampleResponse {
ExampleMessage example = 1;
bool success = 2;
string message = 3;
}
message GetExampleRequest {
int32 id = 1;
}
message GetExampleResponse {
ExampleMessage example = 1;
bool found = 2;
}
message ListExamplesRequest {
int32 page_size = 1;
string page_token = 2;
string filter = 3;
}
message ListExamplesResponse {
repeated ExampleMessage examples = 1;
string next_page_token = 2;
int32 total_count = 3;
}
import 'package:grpc/grpc.dart';
import 'lib/proto/example/v1/example.pbgrpc.dart';
Future<void> main() async {
// Create a gRPC client
final channel = ClientChannel('localhost',
port: 50051,
options: const ChannelOptions(
credentials: ChannelCredentials.insecure(),
),
);
final client = ExampleServiceClient(channel);
try {
// Create a new example message
final example = ExampleMessage()
..id = 1
..name = 'John Doe'
..email = 'john@example.com'
..tags.addAll(['developer', 'dart'])
..description = 'Example user for testing';
// Make RPC call
final request = CreateExampleRequest()..example = example;
final response = await client.createExample(request);
if (response.success) {
print('Created example: ${response.example.name}');
}
// List examples with pagination
final listRequest = ListExamplesRequest()
..pageSize = 10
..filter = 'developer';
final listResponse = await client.listExamples(listRequest);
print('Found ${listResponse.examples.length} examples');
// Watch for streaming updates
final watchRequest = ListExamplesRequest()..filter = 'live';
await for (final update in client.watchExamples(watchRequest)) {
print('Received update: ${update.name}');
}
} finally {
await channel.shutdown();
}
}

For each .proto file, Dart generates:

  • *.pb.dart - Message classes with getters/setters
  • *.pbenum.dart - Enum definitions
  • *.pbgrpc.dart - gRPC client and server stubs (if services defined)
  • *.pbjson.dart - JSON serialization support
name: my_app
dependencies:
protobuf: ^3.1.0
grpc: ^3.2.4
dev_dependencies:
flutter_test:
sdk: flutter
# Generated proto files location
# Assuming outputPath = "lib/proto"

Add to your build.yaml if using build_runner:

targets:
$default:
sources:
exclude:
- lib/proto/**
  1. Output Path: Use lib/proto for Flutter apps to ensure proper imports
  2. Package Name: Match your Dart package name for consistency
  3. IDE Support: Enable generate_kythe_info for better IDE integration
  4. Error Handling: Always wrap gRPC calls in try-catch blocks
  5. Channel Management: Reuse channels and close them properly
  6. Streaming: Use async generators for server-side streaming
Terminal window
cd examples/dart-example
nix develop
dart pub get
dart run lib/main.dart
dart test

Ensure your import paths match the output directory:

// If outputPath = "lib/proto"
import 'package:my_app/proto/example/v1/example.pb.dart';

For local development, use insecure credentials:

final channel = ClientChannel(
'localhost',
port: 50051,
options: const ChannelOptions(
credentials: ChannelCredentials.insecure(),
),
);

For Flutter web, use gRPC-Web instead:

final channel = GrpcOrGrpcWebClientChannel.toSeparateEndpoints(
grpcHost: 'localhost',
grpcPort: 50051,
grpcWebHost: 'localhost',
grpcWebPort: 8080,
grpcTransportSecure: false,
);
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    bufrnix = {
      url = "github:conneroisu/bufrnix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = {
    self,
    nixpkgs,
    flake-utils,
    bufrnix,
  }:
    flake-utils.lib.eachDefaultSystem (system: let
      pkgs = nixpkgs.legacyPackages.${system};
    in {
      devShells.default = pkgs.mkShell {
        packages = [
          pkgs.dart
          pkgs.protobuf
        ];
      };
      packages = {
        default = bufrnix.lib.mkBufrnixPackage {
          inherit pkgs;
          config = {
            root = ./.;
            protoc = {
              sourceDirectories = ["./proto"];
              includeDirectories = ["./proto"];
              files = ["./proto/example/v1/example.proto"];
            };
            languages.dart = {
              enable = true;
              outputPath = "proto/gen/dart";
              packageName = "example_proto";
              options = [];
              grpc = {
                enable = true;
                options = [];
              };
            };
          };
        };
      };
    });
}