Dart Unhandled Exception: setState() called after dispose() in flutter - flutter

I have a page on which I display data via BloC. I added the isLoading variable to display the loading indicator, which is true after two seconds, this value changes to false and the loading indicator disappears and the data is displayed. But I got an error because I use setState(). Tell me how to fix this error?
Widget _child(Size size, BuildContext context, double topPadding) {
return BlocBuilder<MycarsCubit, MycarsState>(
builder: (context, stateElectricvehicles) {
final ElectricvehiclesCubit cubit =
BlocProvider.of<ElectricvehiclesCubit>(context);
if (stateElectricvehicles is MycarsInitial) {
carNumber.text = stateElectricvehicles.number;
carNumber.selection = TextSelection.fromPosition(
TextPosition(offset: carNumber.text.length),
);
if (stateElectricvehicles.id == 0) {
savedCarId = stateElectricvehicles.id;
}
Future.delayed(const Duration(seconds: 2), () {
setState(() {
isLoading = false;
});
});
final SharedPrefs prefs = SharedPrefs();
return FutureBuilder<int?>(

Always add a check mounted (available in StatefulWidget) before calling setState in async function.
Future.delayed(const Duration(seconds: 2), () {
if(mounted) {
setState(() {
isLoading = false;
});
}
});

See if this solves it:
Future.delayed(const Duration(seconds: 2), () {
// Wrap setState in an if:
if (mounted) {
setState(() {
isLoading = false;
});
}
});
This should make sure setState() is not called unless the context is still mounted.
In later versions of flutter lints, you will get a warning about this, saying something like "Do not use context over async gaps!". Meaning if you have an async delay, i.e. an "await", you have to check afterwards if the context is still alive before you use it. 🙂

Related

How can I call a method seconds after the first build in flutter?

I want to call a loading screen widget that has a column with an image, a text and a loading indicator, 5 seconds after the screen is built there should called a setState-method that changes a boolean value[foundOpponent] and with that the screen (the text should be changed into "found opponent" and the indicator into an Icon) after that there should be a delay of 2 seconds and then a Navigator.push to another screen.
I have already looked at https://stackoverflow.com/questions/49466556/flutter-run-method-on-widget-build-complete and tried most of the explained methods and it somewhat worked but the screen of the virtual phone was extremely lagging , the image that should get built in the build method does not even get displayed and while the image is loading the method that should get called after the build is being executed.
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
sleep(const Duration(seconds: 10));
setState(() {
foundOpponent = true;
});
sleep(const Duration(milliseconds: 2500));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const QuestionMainPage()),
);
});
}
I've tried the following methods:
WidgetsBinding.instance
.addPostFrameCallback((_) => yourFunction(context));
SchedulerBinding.instance.addPostFrameCallback((_) => yourFunction(context));
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance.addPostFrameCallback((_) => yourFunction(context));
}
Future<void> _runsAfterBuild() async {
await Future((){}); // <-- Dummy await
// This code runs after build
}
#override
Widget build(BuildContext context) {
_runsAfterBuild();
return Container();
}
Try using Future.delayed and make sure to put await
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await Future.delayed(Duration(seconds: 10));
//await task a
await Future.delayed(Duration(seconds: 1));
//await task B
});
}

In flutter I need to hide button after 1 mins

I need to hide button after 1 mins but while reloading and again restarting application it will not show again once it hide it will not repeated
Use shared_preferences (or any persist memory) to persist the state of the button over restarts.
See Store key-value data on disk for details.
You need to use db to handle restarting application case.
You can use shared_preferences to store a flag about visibility.
class _GState extends State<G> {
static const String _buttonVisibilityKey = "DoneWIthButtonX";
bool showButton = false;
#override
void initState() {
super.initState();
_buttonActionChecker().then((value) async {
// return false if button never showed up, activate the timer to hide
print(value);
if (!value) {
showButton = true;
setState(() {});
Future.delayed(Duration(minutes: 1)).then((value) async {
showButton = false;
SharedPreferences.getInstance()
.then((value) => value.setBool(_buttonVisibilityKey, true));
setState(() {});
});
}
});
}
#override
void dispose() {
super.dispose();
}
/// is the button have been shown already return true
Future<bool> _buttonActionChecker() async {
return SharedPreferences.getInstance().then((value) {
return value.getBool(_buttonVisibilityKey) ?? false;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: showButton
? FloatingActionButton(
onPressed: () {
setState(() {});
},
)
: null,
);
}
}

call one asynchronous function when the first one runs flutter

I have two asynchronous functions, one returns a popup, the other makes a permission request. I call them in the init method. But they are called simultaneously, i.e. the first window appears and immediately the second. How do I fix this?
class _MyAppState extends State<MyApp> {
final keyIsFirstLoaded = 'is_first_loaded';
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
final context = MyApp.navKey.currentState.overlay.context;
await showDialogIfFirstLoaded(context);
await initPlatformState();
});
}
showDialogIfFirstLoaded(BuildContext context, prefs) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isFirstLoaded = prefs.getBool(keyIsFirstLoaded);
if (isFirstLoaded == null) {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return new AlertDialog(
// title: new Text("title"),
content: new Text("//"),
actions: <Widget>[
new FlatButton(
child: new Text(".."),
onPressed: () {
Navigator.of(context).pop();
prefs.setBool(keyIsFirstLoaded, false);
},
),
],
);
},
);
}
}
initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
print('Initialization done');
final _isRunning = await BackgroundLocator.isRegisterLocationUpdate();
setState(() {
isRunning = _isRunning;
});
onStart();
print('Running ${isRunning.toString()}');
}
add return to showDialog statement, you're not returning a Future so await isn't doing anything
Personal advice: always specify return types, cause if you don't, you get dynamic return type. If you do specify it, the IDE/dart analysis server will help you with problems such as this one.

getting code to run upon widget creation flutter

I have a flutter camera app and am able to get a recorded video to play. The problem is, I am only able to get it to play when a button is pressed. How do I get the code to run when the widget(screen) is created instead of when the button is pressed so I don't have to press a button to get it to play? Here is my code:
Here is the code for when the button is pressed:
//raised button
RaisedButton(
onPressed: () {stopButtonPressed();},
//stopButtonPressed
void stopButtonPressed() {
print('stopButtonPressed hit');
stopVideoRecording().then((_) {
print('StopVideoRecording complete');
});
}
//stopVideoRecording
Future<void> stopVideoRecording() async {
print('stopVideoRecording hit');
await _startVideoPlayer();
}
//_startVideoPlayer
Future<void> _startVideoPlayer() async {
print('_startVideoPlayer hit');
print(Provider.of<SendDataModel>(context, listen: false).displayImageVideo());
final VideoPlayerController vcontroller =
VideoPlayerController.file(File(Provider.of<SendDataModel>(context, listen: false).displayImageVideo()));
videoPlayerListener = () {
if (videoController != null && videoController.value.size != null) {
if (mounted) setState(() {});
videoController.removeListener(videoPlayerListener);
}
};
vcontroller.addListener(videoPlayerListener);
await vcontroller.setLooping(true);
await vcontroller.initialize();
await videoController?.dispose();
if (mounted) {
setState(() {
//saveImagePath = null;
videoController = vcontroller;
});
}
await vcontroller.play();
} //startVideoPlayer
Thanks!
You can call the function from initState(). initState() is called only once when the StatefulWidget is inserted into the Widget tree, so it's a good place to initialize variables or do what you're trying to do.
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
// Do anything you need done here
_startVideoPlayer();
// If you want a slight delay, use Future.delayed
Future.delayed(Duration(seconds: 1), (){
_startVideoPlayer();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
// rest of app

Flutter show a widget while an async await task is being executed

I have the following onTap event which executes a function:
onTap: () async {
await firestoreUserData.updateProfilePicture(url);
})
Is it possible to show some kind of loading indicator (a widget, like a ProgressIndicator) while the await is being executed? I cannot use a futurebuilder, as this is an onTap event.
Just set the loading state (define it in the class).
onTap: () async {
setState(() { _isLoading = true });//show loader
await firestoreUserData.updateProfilePicture(url);//wait for update
setState(() { _isLoading = false });//hide loader
})
In your build:
Widget build(BuildContext context) {
if(_isLoading)
return Text("Loading");
else
//your stuff here.
}