New to Dart|Flutter.
I can’t determine why the Flutter StreamBuilder
doesn’t work with the gRPC async watch
method even though I’ve confirmed that:
- the gRPC
watch
method works (using a standalone Dart app) - I can use
StreamBuilder
with very a similiar asyncpingpong
method.
The known working Dart gRPC client:
import "package:grpc/grpc.dart";
import "package:ackalui/generated/health.pbgrpc.dart";
class Client {
final address = "[REDACTED]";
final port = 443;
late HealthClient _stub;
Client() {
_createChannel();
}
_createChannel() {
final channel = ClientChannel(
address,
port: port,
options: ChannelOptions(
credentials: ChannelCredentials.secure(),
),
);
_stub = HealthClient(channel);
}
Stream<String> watch() async* {
try {
final rqst = HealthCheckRequest();
var resp = await _stub.watch(rqst);
print("[Client:watch] await");
await for (var item in resp) {
print("[Client:watch] yielding");
yield item.status.toString();
}
} on GrpcError catch (e) {
print("gRPC error: ${e.code} -- ${e.message}");
// rethrow;
yield* Stream.error(e);
} catch (e) {
print("Unexpected error: ${e}");
// rethrow;
yield* Stream.error(e);
}
}
}
NOTE I’ve tried
rethrow
andyield* Stream.error(e)
on thecatch
‘es with no evident difference in behavior.
I’m able to run a Dart client that streams watch
gRPC responses from a server.
Future<void> main(List<String> args) async {
// Test gRPC Watch
final client = Client();
var resp = await client.watch();
await for (var status in resp) {
print(status);
}
}
Logs:
[Client:watch] await
[Client:watch] yielding
UNKNOWN
[Client:watch] yielding
SERVING
[Client:watch] yielding
NOT_SERVING
[Client:watch] yielding
SERVING
[Client:watch] yielding
UNKNOWN
...
But the Flutter Widget that I want to stream the status
values hits the final else
and returns CircularProgressIndicator
and the gRPC watch
method does not proceed beyond the logged [Client:watch] await
step.
StreamBuilder<String>(
stream: _client.watch(),
builder: (context, snapshot) {
print("builder");
if (snapshot.hasError) {
print("[builder] error");
return Text('Error: ${snapshot.error}');
} else if (snapshot.connectionState == ConnectionState.waiting) {
print("[builder] waiting");
return const CircularProgressIndicator();
} else {
print("[builder] else");
return Text(
snapshot.data ?? '[empty]',
style: Theme.of(context).textTheme.headlineMedium,
);
}
},
)
initialized with:
class _MyHomePageState extends State<MyHomePage> {
late Client _client;
late TestClient _testClient;
@override initState() {
super.initState();
_client = Client();
_testClient = TestClient();
}
@override
Widget build(BuildContext context) {
...
}
}
Logs:
builder
waiting
[Client:watch] await
NOTE Unlike the (pure) Dart example, it’s not proceeding to
[Client:watch] yielding
I have a working Flutter app that streams (similarly yield
‘ed String
‘s):
StreamBuilder<String>(
stream: _testClient.pingpong(1000),
builder: (context, snapshot) {
print("builder");
if (snapshot.hasError) {
print("[builder] error");
return Text('Error: ${snapshot.error}');
} else if (snapshot.connectionState == ConnectionState.waiting) {
print("[builder] waiting");
return const CircularProgressIndicator();
} else {
print("[builder] else");
return Text(
"Value: ${snapshot.data}",
style: Theme.of(context).textTheme.headlineMedium,
);
}
},
)