Reputation: 14276
I want to show a SnackBar
once a Stream
of String
sends a new String
.
I tried to place a StreamBuilder
inside a StatefulWidget
, in order to be able to call Scaffold.of()
(since it's another context
now). but then I get this error:
The following assertion was thrown building StreamBuilder(dirty, state: _StreamBuilderBaseState>#7f258): setState() or markNeedsBuild() called during build. This Scaffold widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase. The widget on which setState() or markNeedsBuild() was called was: Scaffold(dependencies: [_LocalizationsScope-[GlobalKey#0e1f6], Directionality, _InheritedTheme, MediaQuery], state: ScaffoldState#3f2aa(tickers: tracking 2 tickers)) The widget which was currently being built when the offending call was made was: StreamBuilder(dirty, state: _StreamBuilderBaseState>#7f258)
How can I solve this?
This a simple code snippet showing the problem:
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: TestHome(),
);
}
}
class TestHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: StreamSnackbar(),
);
}
}
class StreamSnackbar extends StatefulWidget {
@override
_StreamSnackbarState createState() => _StreamSnackbarState();
}
class _StreamSnackbarState extends State<StreamSnackbar> {
final status = StreamController<String>();
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
status.add('Test');
},
child: Text('Press me to trigger the Snackbar!'),
),
StreamBuilder<String>(
stream: status.stream,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(snapshot.data)));
}
return Container(
height: 0,
width: 0,
); // just because we need to return a Widget
},
),
],
),
),
);
}
}
Upvotes: 2
Views: 2292
Reputation: 29
if (snapshot.hasError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${snapshot.error.toString()}'),
),
);
}
In Your StreamBuilder You have to use like this
Upvotes: 0
Reputation: 657476
There is no need to use a StreamBuilder
. Showing the SnackBar
has to be done outside of sync build()
execution anyway.
@override
void initState() {
super.initState();
status.stream.forEach((e) =>
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(e))));
}
@override
void dispose() {
status.close();
super.dispose();
}
Upvotes: 5