I have a Provider model such as
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. Here I am using their example together with my 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):
Now, when I click on the send button, it calls _sendMessage() (code above). And then since the StreamBuilder() hasData it runs this line (code above):
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With