Rebuild a FutureBuilder - flutter

I am trying to rebuild a FutureBuilder, in my case it is for the purpose of changing the camera that is shown to the user. For that I have to run the Future, the FutureBuilder uses again.
The Future currently looks like this:
body: FutureBuilder(
future: _initializeCameraControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return CameraPreview(_cameraController);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
The Future it is running is this one:
Future<void> _initializeCameraControllerFuture;
#override
void initState() {
super.initState();
if (camerafrontback != true) {
_cameraController =
CameraController(widget.camera[0], ResolutionPreset.veryHigh);
}else{
_cameraController =
CameraController(widget.camera[1], ResolutionPreset.veryHigh);
}
_initializeCameraControllerFuture = _cameraController.initialize();
}
I have a button that should trigger this rebuild of the FutureBuilder, it is also changing the 'camerafrontback' Boolean, so that a different camera is used. It is shown below:
IconButton(
onPressed: () async {
if (camerafrontback == true){
setState(() {
camerafrontback = false;
});
}else{
setState(() {
camerafrontback = true;
});
};
},
At the end, before the last bracket, there must be a statement added that triggers the rebuilds of whole FutureBuilder, but I couldn't find one that suits to my code.
Thanks in advance for your answers!

You need to reinitialize_initializeCameraControllerFuture.
You can do it something like this
#override
void initState() {
super.initState();
_init();
}
...
_init() {
if (camerafrontback) {
_cameraController =
CameraController(widget.camera[1], ResolutionPreset.veryHigh);
} else {
_cameraController =
CameraController(widget.camera[0], ResolutionPreset.veryHigh);
}
_initializeCameraControllerFuture = _cameraController.initialize();
}
...
IconButton(
onPressed: () async {
if (camerafrontback) {
camerafrontback = false;
setState(_init());
} else {
camerafrontback = true;
setState(_init);
}
},
),

to full rebuild your screen you can just replace the screen with the same screen
Navigator.pushReplacement(context,
MaterialPageRoute(
builder: (BuildContext context) =>
*SamePage*()
)
);

Related

How to conditionally show Get.bottomSheet when starting app

I am trying to popup Get.bottomSheet when starting app.
I did it like bottom code.
#override
void initState() {
_popUpBottomBanner();
}
void _popUpBottomBanner() {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
Get.bottomSheet(
...
);
},
);
}
But I want to judge show or hide bottom sheet by API result.
So I changed Code like below.
void _popUpBottomBanner() {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
FutureBuilder<CustomListVO>(
future: Api().getBanners(...),
builder: (BuildContext _, AsyncSnapshot<CustomListVO> snapshot) {
if (snapshot.hasError) return;
if (!snapshot.hasData) return;
if (snapshot.data?.list?.isNotEmpty == true) {
Get.bottomSheet(
...
);
}
return;
},
);
},
);
}
Despite confirming API result arriving properly,
bottomSheet isn't showing.
What is the problem with this code?
Thanks for reading :D
====== resolved like below ========
#override
Widget build(BuildContext context) {
return FutureBuilder<CustomListVO>(
future: Api().getBanners(...),
builder: (BuildContext _, AsyncSnapshot<CustomListVO> snapshot) {
if (snapshot.data?.list?.isNotEmpty == true) {
Get.bottomSheet(
...
);
}
return HomePage();
}
);
}
you need to show bottom sheet that is not required to use Futurebuilder so you can use it like this :
var result = await Api().getBanners(...);
if (result.data?.list?.isNotEmpty == true) {
Get.bottomSheet(
...
);
}

StreamBuilder / ChangeNotifierProvider- setState() or markNeedsBuild() called during build

Streambuilder, ChangeNotifier and Consumer cannot figure out how to use correctly. Flutter
I've tried and tried and tried, I've searched a lot but I cannot figure this out:
I'm using a Streambuilder this should update a ChangeNotifier that should trigger rebuild in my Consumer widget. Supposedly...
but even if I call the provider with the (listen: false) option I've got this error
The following assertion was thrown while dispatching notifications for
HealthCheckDataNotifier: setState() or markNeedsBuild() called during
build. the widget which was currently being built when the offending call was made was:
StreamBuilder<List>
Important: I cannot create the stream sooner because I need to collect other informations before reading firebase, see (userMember: userMember)
Widget build(BuildContext context) {
return MultiProvider(
providers: [
/// I have other provider...
ChangeNotifierProvider<HealthCheckDataNotifier>(create: (context) => HealthCheckDataNotifier())
],
child: MaterialApp(...
then my Change notifier look like this
class HealthCheckDataNotifier extends ChangeNotifier {
HealthCheckData healthCheckData = HealthCheckData(
nonCrewMember: false,
dateTime: DateTime.now(),
cleared: false,
);
void upDate(HealthCheckData _healthCheckData) {
healthCheckData = _healthCheckData;
notifyListeners();
}
}
then the Streambuilder
return StreamBuilder<List<HealthCheckData>>(
stream: HeathCheckService(userMember: userMember).healthCheckData,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
if (snapshot.data!.isNotEmpty) {
healthCheckData = snapshot.data?.first;
}
if (healthCheckData != null) {
timeDifference = healthCheckData!.dateTime.difference(DateTime.now()).inHours;
_cleared = healthCheckData!.cleared;
if (timeDifference < -12) {
healthCheckData!.cleared = false;
_cleared = false;
}
///The problem is here but don't know where to put this or how should be done
Provider.of<HealthCheckDataNotifier>(context, listen: false).upDate(healthCheckData!);
}
}
return Builder(builder: (context) {
return Provider<HealthCheckData?>.value(
value: healthCheckData,
builder: (BuildContext context, _) {
return const HealthButton();
},
);
});
} else {
return const Text('checking health'); //Scaffold(body: Loading(message: 'checking...'));
}
});
and finally the Consumer (note: the consumer is on another Route)
return Consumer<HealthCheckDataNotifier>(
builder: (context, hN, _) {
if (hN.healthCheckData.cleared) {
_cleared = true;
return Container(
color: _cleared ? Colors.green : Colors.amber[900],
Hope is enough clear,
Thank you so very much for your time!
it is not possible to setState(or anything that trigger rerender) in the builder callback
just like you don't setState in React render
const A =()=>{
const [state, setState] = useState([])
return (
<div>
{setState([])}
<p>will not work</p>
</div>
)
}
it will not work for obvious reason, render --> setState --> render --> setState --> (infinite loop)
so the solution is similar to how we do it in React, move them to useEffect
(example using firebase onAuthChange)
class _MyAppState extends Stateful<MyApp> {
StreamSubscription<User?>? _userStream;
var _waiting = true;
User? _user;
#override
void initState() {
super.initState();
_userStream = FirebaseAuth.instance.authStateChanges().listen((user) async {
setState(() {
_waiting = false;
_user = user;
});
}, onError: (error) {
setState(() {
_waiting = false;
});
});
}
#override
void dispose() {
super.dispose();
_userStream?.cancel();
}
#override
Widget build(context) {
return Container()
}
}

flutter - FutureBuilder auto rebuild each time press a button in the screen

I try to use FutureBuilder in Flutter to wait ulti my initState is finished then buil the UI for the app.
But when the app is running, the screen keep rebuilding each time I press another button (the button does totally different thing).
Future loadUser() async {
String jsonString = await storage.read(key: "jwt");
final jsonResponse = json.decode(jsonString);
loggedUser = new LoggedUser.fromJson(jsonResponse);
print(loggedUser.token);
getProfile();
getJourneyByUserId()
.then((receivedList){
addRanges(receivedList);});
}
Future<List<Journey>>getJourneyByUserId() async {
var res = await http.get(
Uri.parse("$baseUrl/journeys/userid=${loggedUser.user.userId}"),
headers: {
'Content_Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ${loggedUser.token}',
},
);
if (res.statusCode == 200) {
print("Get journeys successfully");
}
var data = jsonDecode(res.body);
List idList = [];
for (var i in data) {
idList.add(i["journeyId"]);
}
for (var i in idList) {
var res = await http.get(
Uri.parse("$baseUrl/journeys/$i"),
);
var data = jsonDecode(res.body);
Journey userJourney = new Journey.fromJson(data);
setState(() {
journeyList.add(userJourney);
});
}
print("Journey ${journeyList.length}");
return journeyList;
}
addRanges(journeyList){
setState(() {
rangeList=[];
});
if (journeyList.isNotEmpty) {
for (var i in journeyList) {
DateTime startDate =
DateTime(i.startDate.year, i.startDate.month, i.startDate.day);
DateTime endDate =
DateTime(i.endDate.year, i.endDate.month, i.endDate.day);
setState(() {
rangeList.add(PickerDateRange(startDate, endDate));
});
}
}
print("Range ${rangeList.length}");
return rangeList;
}
returnRange() {
List<PickerDateRange> list = [];
for(int i =0; i<rangeList.length;i++){
list.add(rangeList[i]);
}
return list;
}
Future functionForBuilder() async {
return await returnRange();
}
//initState function
#override
void initState() {
super.initState();
loadUser();
functionForBuilder();
}
//build the UI
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("$_name's Profile",style: TextStyle(color: kColorPalette4),),
centerTitle: true,
),
body: Container(
child: FutureBuilder(
future: functionForBuilder(),
builder: (BuildContext context,AsyncSnapshot snapshot){
//here I set the condition for each case of snapshot
}
I have read some documents say that I should assign the functionForBuilder() to a Future variable when initState then use it in the future child of FutureBuilder. Example:
Future _future;
//initState function
#override
void initState() {
super.initState();
loadUser();
_future=functionForBuilder();
}
// then with the FutureBuilder
future: _future
With this way the screen is not rebuild anymore but my function returnRange() seems like not running as my expextation (I called the returnRange() once in the build() function).
Thanks in advance for your answer!
Whenever you assign to the _future variable again, you must do that inside a setState block, otherwise the widget will not rebuild with the new future.
For example:
void updateData() {
setState(() {
_future = functionForBuilder();
});
}
If you use FutureBuilder, it rebuild items again and again.
Try two ways:
Don't use `future: functionForBuilder(), comment it.
Remove FutureBuilder(), simply use Container().
And let me know any issue?
Code:
call your future in the initstate method not in the build as shown in the example.
class MyPage extends StatefulWidget { #override State<MyPage> createState() => _MyPageState(); } class _MyPageState extends State<MyPage> { // Declare a variable. late final Future<int> _future; #override void initState() { super.initState(); _future = _calculate(); // Assign your Future to it. } // This is your actual Future. Future<int> _calculate() => Future.delayed(Duration(seconds: 3), () => 42); #override Widget build(BuildContext context) { return Scaffold( body: FutureBuilder<int>( future: _future, // Use your variable here (not the actual Future) builder: (_, snapshot) { if (snapshot.hasData) return Text('Value = ${snapshot.data!}'); return Text('Loading...'); }, ), ); } }

Adding data to existing list from initState in Flutter (Async)

I try to load specific data from my Sembast database to the view on init and add it to an existing list. Why this code does not work?
The data exists, but could it be that my code will never called? I´ve edited the code with print. print("3") does not fire.
bool _checkConfiguration() => true;
#override
void initState() {
_currentDate = widget._currentDate;
_markedDateMap = widget._markedDateMap;
if (_checkConfiguration()) {
print("1"); // FIRES
Future.delayed(Duration(seconds: 2), () {
final calendarEntriesDataInitial =
Provider.of<CalendarEntries>(context, listen: false);
print("2"); // FIRES
FutureBuilder<List<CalendarEntry>>(
future: calendarEntriesDataInitial.getAll(),
builder: (BuildContext context,
AsyncSnapshot<List<CalendarEntry>> snapshot) {
print("3"); // NOT FIRES. HERE´S A PROBLEM!
if (snapshot.hasData &&
snapshot.connectionState != ConnectionState.waiting) {
for (final entry in snapshot.data) {
setState(() {
_markedDateMap.add(
new DateTime(entry.dateTime),
new Event(
date: new DateTime(entry.dateTime),
title: 'Event 5',
icon: _eventIcon,
));
});
}
}
});
});
}
super.initState();
}
You have to use setState() for showing updated UI in map
setState(() {
_markedDateMap.add(...)
});
I found a solution: The FutureBuilder is a widget and can only by used in the widget three. Therefore I use another way to get my required values:
#override
void initState() {
_currentDate = widget._currentDate;
_markedDateMap = widget._markedDateMap;
Future.delayed(Duration.zero, () {
final calendarEntriesDataInitial =
Provider.of<CalendarEntries>(context, listen: false);
calendarEntriesDataInitial.getAll().then((value) {
if (value == null || value.isEmpty) {
return;
}
for (final entry in value) {
if (entry.dateTime != null) {
final date = DateTime.parse(entry.dateTime);
setState(() {
_markedDateMap.add(
new DateTime(date.year, date.month, date.day),
new Event(
date: new DateTime(date.year, date.month, date.day),
title: entry.servicePartner,
icon: _eventIcon,
));
});
}
}
});
});
super.initState();
}

Is there any way to listen device location on or off?

I used Geolocator plugin. It's working. but not display a message immediately. the message is displayed like after 2,3 minutes. It does not display real-time.
Geolocator().isLocationServiceEnabled() is future, can't listen right?
FutureBuilder<bool>(
future: Geolocator().isLocationServiceEnabled(),
builder: (context,snapshot){
if(snapshot.hasData){
if(!snapshot.data)
return CustomText(
text: 'GPS Interrupted',
);
else
return Container();
} else{
return Container();
}
}
)
I used this widget like this,
bottom: PreferredSize(child: FutureBuilder<bool>(
I found this way, It's working.
#override
void initState() {
super.initState();
const Duration checkingTime = Duration(seconds:5);
_timer = Timer.periodic(checkingTime, (Timer t) => checkLocation());//Using GeoLocator
}
#override
void dispose() {
super.dispose();
if( _timer != null) _timer.cancel();
}
void checkLocation(){
Geolocator().isLocationServiceEnabled().then(locationService);
}
void locationService(bool result){
if(!mounted) return;
setState(() {
if(result)
isLocationEnable = true;
else
isLocationEnable = false;
});
}