Flutter Image assets and Stream values only render after user interactions - flutter

When opening the application for the first time images and stream values are not rendering until I tap on the screen or start scrolling. Made sure that the snapshot.data is printing out the correct values before returning the widget in the stream builder.
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
'assets/profile_image.png',
height: 36.0,
width: 36.0,
),
SizedBox(width: 10.0),
StreamBuilder<String>(
stream: context.read<UserBloc>().balanceStream,
builder: (context, snapshot) {
print('ConnectionState is ${snapshot.connectionState}');
switch (snapshot.connectionState) {
case ConnectionState.active:
print('Value is ${snapshot.data}');
return NonScalingTextView(
"\$ ${snapshot.data}",
style: TitleTiny,
);
break;
default:
return Container();
}
},
),
],
);
Performing some user interaction or hot reloading or hot restarting the application after first launch, the widgets render as expected.

I had an Authentication Bloc parenting the Material App. Inside I had a User Bloc created on successful Auth. I had the above issues when this was the case. The issue was only happening on the Widgets rendered based on the states emitted by the User Bloc. So rather than returning these widgets on state changes I changed my listener to navigate to these widgets on state changes. Now in order to call on my User Bloc from this navigated route, I had to move the User Bloc on top of the Material App. And doing this solved my issue.
This solved it but I still don't know what caused the former architecture to fail. If anyone can explain me, it'd be great.

Related

Generic filter Flutter

Goodmorning,
I'm developing an app with flutter but I'm facing some problems with Provider (I think something miss in my knowledge).
My app fetch data from my API and displays them in listview.
In whole app I have different screen which displays different data type in listview and now I want to create filtering logic.
To avoid rewrite same code multiple times I thought to create one screen to reuse for filtering purposes but I'm facing problems with state management.
What I did:
create base model for filter information
`
enum FilterWidget { TEXT_FIELD, DROPDOWN } //to resolve necessary Widget with getWidget() (to implement)
class FilterBaseModel with ChangeNotifier {
String? value= 'Hello';
FilterWidget? widgetType;
FilterBaseModel(this.value, this.widgetType);
onChange() {
value= value== 'Hello' ? 'HelloChange' : 'Hello';
notifyListeners();
}
}
`
One screen for display filters depending on request
List<FilterBaseModel> filters = [];
FilterScreen() {
//Provided from caller. Now here for test purposes
filters.add(FilterBaseModel('Filter1', FilterWidget.TEXT_FIELD));
filters.add(FilterBaseModel('Filter2', FilterWidget.TEXT_FIELD));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
minimum: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
child: SingleChildScrollView(
child: Container(
height: 400,
child: Column(
children: filters
.map(
(e) => Consumer<FilterBaseModel>(
builder: (_, filter, child) =>
ChangeNotifierProvider.value(
value: filter,
child: CustomTextField(
`your text` initialText: e.value,
onTap: () {
e.onChange();
filter.onChange();
},
),
),
),
)
.toList(),
))),
),
);
}
`
The problem is in Consumer and ChangeNotifier.value.
Screen works quite well: widget are displayed and callback are called, what is wrong? I need to use onChange method of both instance to update UI otherwhise method was called but widget is not rebuilt.
I know that probably putting consumer there is not right way but I tried also to put outside but doesn't work.
I expect to have one filter screen which receives in input filters list information, display them, handle their state managment and return their value.
P.S: this code now works, but I know is not the right way
Thank you for help!
EDIT
Have same behaviour without ChangeNotifierProvider.value. Therefore I'm more confused than before because still persist the double call to onChange for correct rebuilding.
More bit confused about ChangeNotifierProvider.value using...

Is it OK to add a listener in the build method in a Stateful widget?

I have a scrollcontroller which I need to add a listener to do some pagination functions on when the user scrolls down on a listview.
Currently, I create the scrollcontroller and a listener in the initState. I'm doing it there, because the scroll controller is actually a PrimaryScrollController and it needs context
var _scrollController = PrimaryScrollController.of(context);
Now I've run into a problem where when my page gets rebuilt for one reason or another the listview will jump to the top.
From what I understand its because on a rebuild, everything is getting rebuild however the initState isn't being run.
SO my solution is to move the scrollcontroller creation into the build method, which seems to be working just fine. However, the listener does not work, unless I also move it into the build method.
Is this ok? Or am I creating potentially many parallel listeners which can increase each time the page gets rebuilt?
If you are looking to listen to the scroll position and do some operations based on the scroll offset you can try a builder named valueListenableBuilder
ValueListenableBuilder<int>(
builder: (BuildContext context, int value, Widget? child) {
return Row(
children: <Widget>[
Text('$value'),
child!,
],
);
},
valueListenable: scrollController.offset,
child: Container(),
)

Any chance to disable the lazy loading using ListView on Flutter?

I've this code:
ListView(
children: episodes.map((Episode episode) {
return EpisodeRow(episide: episode)
);
I understand that the intentions for built-in lazy loading is good on flutter, however I really need each widget to be loaded on the screen because each widget must download a file and store it for use when the device is without internet connection.
Why don't you simply download all necessary files when the screen is loaded inside of the initState method, so that you do not need to care about lazy loading destroying the user experience in case the internet connection breaks down.
A solution would be to use a SingleChildScrollView with a Column widget:
SingleChildScrollView(
child: Column(
children: episodes.map((Episode episode) {
return EpisodeRow(episide: episode);
}).toList(),
),
);

Flutter - How to do an infinite card feed?

I want to display one by one each document of a collection from Firestore. A document is a card containing an image and a text with a button to show the next card. Basically, my app should have the same behavior as Tinder: display one card then the next one, and never twice the same.
At the moment, I made a StreamBuilder that call the last 10 cards in a Stack so each card is on top of each other (hiding the next one).
But with this solution I have 2 problems :
I don't know how to display the next item when the user tap on the
next button. The current card has to disappear to let the next one being visible.
The StreamBuilder call only 10 cards, I would like to load 10 more
cards when the user is almost at the end (at the card number 8 for
example) so the user don't even realize the loading in the background.
Maybe the StreamBuilder is not the solution at all. But I have no idea what is the best way to do it.
StreamBuilder(
stream: firestore.collection('Feed').orderBy('date').limit(10).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
var _docs = snapshot.data.docs;
if (_docs.isEmpty)
return Center(
child: Text("There is no more cards"),
);
return Scaffold(
body: Stack(
children: _docs.map((document) {
return Container(
color: Colors.white,
child: CardItem(...),
);
}).toList(),
),
);
});

next Page stream is not update on Flutter

I used two pages. and I added StreamBuilder in my first page and I passed snapshot.data to next Page. but when value change in 2nd-page value is not changing. How to fix it? I can't call streamBuilder in both pages because it's meaning two read in firebase. Is there any way to create singleton for services and access anywhere?
StreamBuilder(
stream: db.getData(),
builder: (context,snapshot){
return Column(
children: <Widget>[
InkWell(
onTap: ()=> Navigator.pushNamed(context, '/nextPage',arguments: Arguments(
data: snapshot.data
)),
)
],
);
},
)
InkWell(
onTap: ()=> Navigator.pushNamed(context, '/nextPage',arguments: Arguments(
data: snapshot.data
),
),
When using the above code, a data snapshot is only sent when you Tap on the InkWell. Meaning unless tapped on the inkwell it will not provide new data to nextPage.
To resolve this issue, I would suggest the following:
In First page
Create ValueNotifier instance to observe changes in the common reference:
// change Cast to type that you receive from firebase, or you can omit the cast
final ValueNotifier<snapshot_data_type> firebaseDataNotifier = ValueNotifier<snapshot_data_type>();
Update the value of firebaseDataNotifier when you receive data from firebase:
StreamBuilder(
stream: db.getData(),
builder: (context,snapshot){
firebaseDataNotifier.value = snapshot.data;
...
Pass the firebaseDataNotifier as data for the nextPage
In the next Page
Wrap the Next page widgets with ValueListenable
ValueListenableBuilder(
valueListenable: valueNotifier,
builder: (context, firebaseData, child) {
// return your next page widget here and use firebaseData instead of snapshot.data
)
Note: Make sure you keep the ValueNotifier instance outside of both widgets so that they can access it without any dependency.