'ListView not displaying item with BLoC pattern
I'm a flutter beginner and I'm currently working on an app that let me take a photo and then update a ListView to display the photo with a little text description under. Both are placed inside a Card. I already managed to make it work properly. Recently I dived into BLoC pattern and I tried to implement it on this app. The problemn is that the ListView doesn't display what goes out of the stream even though snapshot contain the data added.
Here is my main :
class MyApp extends StatelessWidget {
final LandscapeBloc _landscapeBloc = LandscapeBloc();
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: <String,WidgetBuilder>{
'/CameraPage': (BuildContext context)=> CameraScreen(cameras,_landscapeBloc),
},
initialRoute: '/',
home: HomePage(_landscapeBloc)
);
}
}
Here is my code for the HomePage that contain the ListView that is exposed to the streamOutput
class HomePage extends StatefulWidget {
final LandscapeBloc _landscapeBloc;
@override
_HomePageState createState() {
return _HomePageState();
}
HomePage(this._landscapeBloc);
}
class _HomePageState extends State<HomePage> {
List<LandscapeCard> list = [];
@override
void dispose() {
widget._landscapeBloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: widget._landscapeBloc.landscapeCardStream,
initialData: list,
builder: (BuildContext context,
AsyncSnapshot<List<LandscapeCard>> snapshot) {
print("[snaphot]" + snapshot.data.toString());
return (Column(children: <Widget>[
Flexible(
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
final LandscapeCard item = snapshot.data[index];
return Dismissible(
key: Key(item.landscapeImagePath),
onDismissed: (direction) {
widget._landscapeBloc.landscapeEvent
.add(DeleteEvent(index));
Scaffold.of(context).showSnackBar(
SnackBar(content: Text("item suprrimé")));
},
child: item);
},
padding: const EdgeInsets.all(10.0)),
),
]));
}));
}
}
I know that the StreamBuilder receive the data cause when I print(snapshot) the list Contain the added photo.
I input data here;
class CardBuilder extends StatefulWidget {
final String _path;
final LandscapeBloc landscapeBloc;
CardBuilder(this._path, this._landscapeBloc);
@override
_CardBuilderState createState() {
// TODO: implement createState
return _CardBuilderState();
}
}
class _CardBuilderState extends State<CardBuilder> {
//TODO: stateful widget pour mis a jour UI quand on tape un texte
final TextEditingController descController = TextEditingController();
@override
void dispose() {
widget.landscapeBloc.dispose();
descController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Column(children: <Widget>[
Align(
alignment: Alignment.centerLeft,
child: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
iconSize: 35.00,
onPressed: widget._onPressedBack),
),
DisplayMedia(widget._path),
Divider(
color: Colors.black,
height: 5,
),
TextField(
autofocus: false,
controller: descController,
maxLines: 5,
decoration: InputDecoration(
filled: false,
fillColor: Colors.black12,
border: InputBorder.none,
hintText: "put a description"),
),
Divider(
color: Colors.black,
height: 5,
),
IconButton(
icon: Icon(Icons.check_circle, color: Colors.black),
iconSize: 35.00,
onPressed: () {
final LandscapeCard landscapeCard= LandscapeCard(descController.text,widget._path);
widget._landscapeBloc.landscapeEvent.add(AddEvent(landscapeCard));
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(
builder: (BuildContext context) => HomePage(widget._landscapeBloc)
),(Route route)=> route==null);
})
]),
));
}
}
here is the BLoc:
class LandscapeBloc {
List<LandscapeCard> list = [];
final StreamController _listLandscapeStateController = StreamController<List<LandscapeCard>>.broadcast();
StreamSink<List<LandscapeCard>> get _landscapeSink => _listLandscapeStateController.sink;
Stream<List<LandscapeCard>> get landscapeCardStream => _listLandscapeStateController.stream;
final StreamController<ManagingListEvent> _listLandscapeEventController = StreamController<ManagingListEvent>.broadcast();
StreamSink<ManagingListEvent> get landscapeEvent => _listLandscapeEventController.sink;
LandscapeBloc(){
_listLandscapeEventController.stream.listen(onManageEvent);
}
void onManageEvent(ManagingListEvent event){
if(event is DeleteEvent){
list.removeAt(event.indexDelete);
}
else{
list.add(event.landscapeCardAdd);
}
_landscapeSink.add(list);
print("[onManageEvent]"+event.landscapeCardAdd.landscapeImagePath+" "+event.landscapeCardAdd.landscapeDesc);
}
void dispose(){
_listLandscapeEventController.close();
_listLandscapeStateController.close();
}
}
and finally the ManageListEvent Class
abstract class ManagingListEvent{
int _indexS;
LandscapeCard _landscapeCardS;;
int get indexDelete => _indexS;
LandscapeCard get landscapeCardAdd => _landscapeCardS;
}
class DeleteEvent extends ManagingListEvent{
DeleteEvent(int index){
super._indexS=index;
}
}
class AddEvent extends ManagingListEvent{
AddEvent(LandscapeCard landscape){
super._landscapeCardS=landscape;
}
}
Basically I want to Navigate to a new Page where I have the photo that i've just Take, add a little description, put it in the sink and then navigate back to HomePage. But the ListView is not rerendered with the new widgets even though it receive it through the snapshot. Thanks in advance for your answers and your time
Solution 1:[1]
The reason why the ListView wasn't updated is because the Stream wasn't populated before the StreamBuilder is rendered on Screen. The StreamBuilder will only rebuild if there's change detected on Stream. You can populate the Stream on initState()
to have the data ready to be displayed.
@override
void initState(){
super.initState();
// Fetch initial List data for Stream
widget._landscapeBloc. fetchLandscapeCardList();
}
On LandscapeBloc, you can add the helper to fetch the initial List.
fetchLandscapeCardList() {
List<LandscapeCard> initList = // Fetch initial data
_listLandscapeStateController.sink.add(initList);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Omatt |