[英]Provider: How can I `notifyListener()` within a `StreamBuilder()`? It causes the error `setState() or markNeedsBuild() called during build`
I have a Provider
model such as provider_model.dart
:我有一个
Provider
模型,例如provider_model.dart
:
import 'package:flutter/material.dart';
class ProviderModel extends ChangeNotifier {
final List<String> _myList = [];
List<String> get myList => [..._myList];
void addItem(String item) {
_myList.add(item);
notifyListeners();
}
}
Now, Flutter documentation shows us how to listen to websockets .现在,Flutter 文档向我们展示了如何监听 websockets 。 Here I am using their example together with my
ProviderModel()
:在这里,我将他们的示例与我的
ProviderModel()
一起使用:
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import './provider_model.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChangeNotifierProvider(
create: (BuildContext context) => ProviderModel(),
child: Scaffold(
body: MyHomePage(),
)),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _controller = TextEditingController();
final _channel = WebSocketChannel.connect(
Uri.parse('wss://echo.websocket.events'),
);
@override
Widget build(BuildContext context) {
return Consumer<ProviderModel>(
builder: (context, provider, _) {
return Column(children: [
TextField(
controller: _controller,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter a search term',
suffixIcon: IconButton(
icon: Icon(
Icons.send,
),
onPressed: _sendMessage)),
),
...provider.myList.map((e) => Text(e)).toList(),
StreamBuilder(
stream: _channel.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
// ERROR HERE!
provider.addItem('I was added');
return Text("Item added");
} else if (snapshot.hasError) {
return Text(snapshot.error as String);
} else {
return CircularProgressIndicator();
}
},
)
]);
},
);
}
void _sendMessage() {
if (_controller.text.isNotEmpty) {
_channel.sink.add(_controller.text);
print('done');
}
}
}
Which the following output (Chrome):以下输出(Chrome):
Now, when I click on the send button, it calls
_sendMessage()
(code above).现在,当我单击发送按钮时,它会调用
_sendMessage()
(上面的代码)。 And then since the StreamBuilder()
hasData
it runs this line (code above):然后由于
StreamBuilder()
hasData
它运行这一行(上面的代码):
provider.addItem('I was added');
However, this is where my error appears, I am getting the following error:但是,这是我的错误出现的地方,我收到以下错误:
The following assertion was thrown while dispatching notifications for ProviderModel:
setState() or markNeedsBuild() called during build.
You can take help from addPostFrameCallback
, but the cost is it will keep rebuilding on every frame, to control this behavior you can use a bool on state class.您可以从
addPostFrameCallback
帮助,但代价是它将在每一帧上继续重建,为了控制这种行为,您可以在状态类上使用 bool。
bool isDone = false;
void addItemH() {
if (!isDone) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Provider.of<ProviderModel>(context, listen: false)
.addItem('I was added');
isDone = true;
});
}
}
And builder和建设者
builder: (context, snapshot) {
if (snapshot.hasData) {
addItemH();
return Text("Item added");
And to control the next insertaion.并控制下一次插入。
void _sendMessage() {
if (_controller.text.isNotEmpty) {
isDone = false;
_channel.sink.add(_controller.text);
print('done');
}
}
}
If you just want to add data only single time, it is better to do inside inside initState.如果你只想单次添加数据,最好在 initState 里面做。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.