I have a problem about using FutureBuilder in Flutter.
With FutureBuilder, the page is continuously rebuilt.
I've omitted the detailed code to write the question. If you want to see additional code, please leave a comment.
To stop this, What can I do?
Future<bool> initLikes() async {
var list = await ApiProvider().post('/RoomSalesInfo/Select/Like', jsonEncode(
{
"userID" : GlobalProfile.loggedInUser.userID,
}
));
return true;
} else {
return false;
}
}
//This is the Code that I use in Widget build
FutureBuilder(
future: initLikes(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
//해당 부분은 data를 아직 받아 오지 못했을때 실행되는 부분을 의미한다.
if (snapshot.hasData == false) {
return SizedBox();
}
//error가 발생하게 될 경우 반환하게 되는 부분
// 데이터를 정상적으로 받아오게 되면 다음 부분을 실행하게 되는 것이다.
else {
return Expanded(
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
controller: _scrollController,
itemCount: GlobalProfile.listForMe.length +1,
itemBuilder: (BuildContext context, int index) {
if(index == GlobalProfile.listForMe.length){
return CupertinoActivityIndicator();
}
else
return Column();
}
),
);
}
})
future: initLikes(),
Don't recomputing this. The new invocation will overwrite the old one. Instead use an initState() to compute it just once into a variable that you reference from "future:..".
Related
How I can show last 5 elements in widget with listview.builder?
Thanks!
if (snapshot.hasData) {
return ListView.builder(
controller: controller,
// shrinkWrap: true,
// reverse: true,
itemCount: snapshot.data!.length ,
itemBuilder: (BuildContext context, int index) {
// controller.jumpTo(controller.position.maxScrollExtent);
if (snapshot.data![index].operatorName == 'barz1' ||
snapshot.data![index].operatorName == 'barz2') {
return Container(
Try this:
if (snapshot.hasData) {
return ListView.builder(
controller: controller,
itemCount: snapshot.data!.length ,
itemBuilder: (BuildContext context, int index) {
if(index > snapshot.data!.length - 5){ /// <--- add this condition
if (snapshot.data![index].operatorName == 'barz1' ||
snapshot.data![index].operatorName == 'barz2') {
return Container(...);
}
}
}
);
}
You can set reverse property true in Listview.builder and set itemcount to 5 to get last 5 elements in widget with ListView.Builder.
ListView.builder(
reverse: true,
itemCount: 5,
itemBuilder: (ctx, index) {
return Container();
},
);
you can use this method
if (snapshot.hasData) {
final originalList=snapshot.data;
final newList=originalList.sublist(originalList.length-5, originalList.length);
return ListView.builder(
controller: controller,
// shrinkWrap: true,
// reverse: true,
itemCount: newList.length ,
itemBuilder: (BuildContext context, int index) {
// controller.jumpTo(controller.position.maxScrollExtent);
if (newList[index].operatorName == 'barz1' ||
newList[index].operatorName == 'barz2') {
return Container(
You can create sublist from original data.
class Fas4 extends StatelessWidget {
const Fas4({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<int>>(
future: Future.value(List.generate(333, (index) => index)),
builder: (context, snapshot) {
//handle other state like error...
if (snapshot.hasData) {
final data = snapshot.data ?? [];
List<int> subList =
data.length <= 5 ? data : data.sublist(data.length - 6);
return ListView.builder(
itemCount: subList.length,
itemBuilder: (context, index) => Text("${subList[index]}"),
);
}
return Text("Loading");
},
),
);
}
}
There is a problem with the first call I have to get the value list of values from the first API to make a second API call. how do update the data to UI. I have two listviews one is for loading from the first API and the second listview is for the second API
After getting a response from the first API listview2 gets a null value getting an exception. because the state is not emitted of FacilityUnitsload
My code is below. What am I missing?
_loadstoragetype(){_bookingCubit.facilitystoragetype());}
_loadfacilityunits(){ _bookingCubit.facilitystorageunits(jsonEncode(
StoragetypeunitsRequest(storagetypelist,locationlist))); }
#override
void didChangeDependencies() {
_loadstoragetype();
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return
MultiBlocProvider(
providers: [
BlocProvider<BookingCubit>.value(value: _bookingCubit),
],
child: Scaffold(
body: BlocProvider.value(
value: _bookingCubit,
child: BlocConsumer<BookingCubit,BookingState>(
builder: (context, state) {
if (state is BookingLoading) {
return Loading();
} else if (state is ErrorState) {
return ErrorTxt(
message: '${state.error}',
ontap: () => _loadtype(),
);
}
if (state is FacilityUnitTypeload) {
if (state.storageTypeModel.isSuccess!) {
_loadfacilityunits();
return ListView.builder(
itemCount: storagetypelist?.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () => '',
child:Text('${storagetypelist[index].name}'),
);
},)
} else {
return Text('Error')
}
}
if (state is FacilityUnitsload) {
if (state.storageTypeUnits.isSuccess!) {
return Expanded(
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: stypeunits.length,
itemBuilder: (BuildContext context, int index) {
return Text('${state.storageTypeUnits.result[index].name}'),
},
));
} else {
return Text('Error')
}
}
return //_buildbookunits();
},
listener: (context, state) {
},
buildWhen: (context, state) {
return state is FacilityUnitTypeload;
},
),
)
);}
Scaffold(body: FutureBuilder(
future: fetchTracks(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.hasData)
{
ListView.builder(
scrollDirection: Axis.vertical,
itemExtent: 130.0,
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: trackes.length,
itemBuilder: (BuildContext context, int index) {
print("test");
return makeCard(snapshot.data[index]);
},
).build(context);
}
else
{
return Center(child: new CircularProgressIndicator());
}
} ));
When i call this Scaffold Future build will call my future function fetchTracks() and get the data in snapshot but it is not entering into itemBuilder function. So futurebuilder return NULL.
Please help me to solve .and Thank you in advance
You're missing a return before ListView.builder. If you don't return it, it won't build it.
FutureBuilder has different snapshot connectionstates which you must handle. Data on the stream is not available until ConnectionState equals done and hasData equals true.
_loadData(context)
{
showModalBottomSheet(
context: context,
builder: (BuildContext bc){
return FutureBuilder(
future: fetchTracks(),
builder: (BuildContext context, AsyncSnapshot<List<MyClass>> snapshot){
if (snapshot.connectionState!=ConnectionState.done)
{
return PleaseWaitWidget();
}
else if(snapshot.hasError)
{
DialogCaller.showErrorDialog(context,"future builder has an error").then((value){});
}
else if (snapshot.connectionState==ConnectionState.done)
{
if(snapshot.hasData){
List<Widget> list = snapshot.data.map((MyClass myClass){
return Card(
child:Wrap(children:<Widget>[
Row(children: [Text(myClass.field1)],),
]));}).toList();
return list;
}
}
});
});
}
I want to build cards on the basis of data received from the future which returns a map. Since cardDetails is being fetched from the backend, it requires some time but while building the cards using ListView.builder, it reaches to itemCount before the data is fetched which makes cardDetails to be null. If I hardcode the value of itemCount then, the error disappears and I get cards as required. Any clues on how to solve this issue would be helpful.
Update: It is going into the snapshot.hasError condition but I'm not able to figure out which error is it
In UI
if (_localStorageService.getStringFromLocalStorage() != 'testFalse')
FutureBuilder(
future: _localStorageService.getMapFromLocalStorage(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
cardDetails = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) {
print("Shared Pref hasData");
return cardDetails == null
? CircularProgressIndicator()
: HomepageCards(
user: widget.user,
cardDetails: cardDetails[
cardDetails.keys.toList()[index]],
);
},
// verify if cardDetails is null to prevent app crash
itemCount:
(cardDetails == null ? 0 : cardDetails.keys.length),
scrollDirection: Axis.vertical,
controller: _controller,
shrinkWrap: true,
);
} else if (snapshot.hasError) {
// TODO: Shimmer skeleton
}
return CircularProgressIndicator();
},
)
else
StreamBuilder<DocumentSnapshot>(
stream: Firestore()
.collection('homepage')
.document(widget.user.uid)
.collection('h')
.document('28032020')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data != null) {
cardDetails = {};
snapshot.data.data.forEach((index, individualDetail) {
cardDetails[index] = individualDetail;
});
_localStorageService
.storeCardInSharedPreference(cardDetails);
cardDetailKeys = snapshot.data.data.keys;
} else if (snapshot.hasError) {
// TODO: Show skeletal shimmer
} else {
// TODO: Convert it to Shimmer with card skeletal layout
CircularProgressIndicator();
}
return cardDetails == null
? CircularProgressIndicator()
: ListView.builder(
itemBuilder: (context, index) {
return HomepageCards(
user: widget.user,
cardDetails:
cardDetails[cardDetails.keys.toList()[index]],
);
},
itemCount: (cardDetailKeys == null
? 0
: cardDetailKeys.length),
scrollDirection: Axis.vertical,
controller: _controller,
shrinkWrap: true,
);
},
)
LocalStorage Service for Shared Preferences
class LocalStorageService {
static SharedPreferences _sharedPreferences;
final String screenkey;
String value;
String _initialSharedValue;
LocalStorageService({#required this.screenkey});
initialiseLocalStorage() async {
_sharedPreferences = await SharedPreferences.getInstance();
persist(screenkey);
}
Future<void> persist(String key) async {
_initialSharedValue = _sharedPreferences?.getString(key);
// will be null if never previously saved
if (_initialSharedValue == null) {
_initialSharedValue = 'testFalse';
}
await _sharedPreferences?.setString(screenkey, _initialSharedValue);
print("share = ${_sharedPreferences?.getString(screenkey)}");
}
storeCardInSharedPreference(Map cardDetails) async {
await _sharedPreferences?.setString(screenkey, json.encode(cardDetails));
}
getMapFromLocalStorage() async {
return await json.decode(_sharedPreferences?.getString(screenkey));
}
String getStringFromLocalStorage() {
return _sharedPreferences?.getString(screenkey);
}
}
This is because regardless of the status of your futurebuilder, Listview is being returned.
If you want to control the status of your futurebuilder, you must put the return inside your if/else/case.
Thus:
FutureBuilder(
future: _localStorageService.getStringFromLocalStorage(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
cardDetails = snapshot.data;
print("number of cards = ${cardDetails.keys.length}");
return ListView.builder(
itemBuilder: (context, index) {
print("card details in futute : ${snapshot.data}");
return cardDetails == null
? CircularProgressIndicator()
: HomepageCards(
user: widget.user,
cardDetails:
cardDetails[cardDetails.keys.toList()[index]],
);
},
// verify if cardDetails is null to prevent app crash
itemCount: (cardDetails == null? 0: cardDetails.keys.length),
scrollDirection: Axis.vertical,
controller: _controller,
shrinkWrap: true,
);
} else if (snapshot.hasError) {
print("Error here in snapshot");
return Center(child:Text("An error has occurred"));
} else {
return CircularProgressIndicator();
}
},
)
I am trying to display items in a ListView using ListView.builder inside a FutureBuilder. My future function for FutureBuilder looks like this:
_fetchListItems() async {
wait() async {
number = await db.getNumber(userId); }
await wait();
List rawFavouriteList = await db.getList(number);
setState((){
rawFavouriteList.forEach((item){
_faouriteList.add(Model.map(item));
}});
return _faouriteList;
}
My FutureBuilder looks like this:
FutureBuilder(
future: _fetchListItems(),
builder:(context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {Container( child: ListView.builder(
itemCount: _faouriteList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text(
'${_faouriteList[index].title}');
}));}})
he following assertion was thrown building FutureBuilder(dirty, state:
I/flutter (24728): _FutureBuilderState#f12a3):
I/flutter (24728): A build function returned null.
I/flutter (24728): The offending widget is: FutureBuilder
I/flutter (24728): Build functions must never return null
Another exception was thrown: A build function returned null.
Note:
I tried to call _fetchListItems() from initState and not use FutureBuilder and that didn't work for me as well.
Here is a link to that case: (Flutter/Dart) Two async methods in initState not working
Please let me know if I should use FutureBuilder or initState to wait for List to load it's data. And how to make it work since none of the methods seem to work for me :(
Your fetch _fetchListItems method is not the problem as far as I can tell.
Your error message is very clear, " a build function returned null". In this case, the method that returns null is the anonymous function passed as the builder argument to the FutureBuilder. You're just missing the return keyword inside the else case because you're just creating the instance of Container but not returning it.
Something like this:
FutureBuilder(
future: _fetchListItems(),
builder:(context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return Container(
child: ListView.builder(
itemCount: _faouriteList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text('${_faouriteList[index].title}');
}
)
);
}
}
)
I don't know if there are any other problems with your code but this should solve the particular error message you are asking about.
It is not necessary to use setState for your case, try this:
Fetch async function
_fetchListItems() async {
number = await db.getNumber(userId);
List rawFavouriteList = await db.getList(number);
List _faouriteList = rawFavouriteList.map((item)=>Model.map(item)).toList();
return _faouriteList;
}
FutureBuilder
FutureBuilder(
future: _fetchListItems(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
Container(
child: ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text('${snapshot.data[index].title}');
}));
}
});