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

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.
}

Related

Dart Unhandled Exception: setState() called after dispose() in 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. 🙂

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 setState not updating child element

I have an InkWell which uses onTap to perform some actions. When the button is tapped, I like an indicator to be shown (in case the action is long-running). However, the setState in the InkWell does not trigger its children to be re-rendered. The code is as follows:
class PrimaryButtonState extends State<PrimaryButton> {
bool _apiCall;
Widget getWidget() {
if(_apiCall) {
return new CircularProgressIndicator();
} else {
return Text(
widget.label,
);
}
}
#override
Widget build(BuildContext context) {
final List<Color> colors = //omitted
return InkWell(
child: Container(
decoration: // omitted
child: getWidget(), // not updated when _apiCall changes !!!!!
),
onTap: () {
setState(() {
_apiCall = true;
});
widget.onTab(context);
setState(() {
_apiCall = false;
});
}
);
}
}
How can I solve this that getWidget returns the correct widget dependent on _apiCall?
EDIT:
The widget.onTap contains the following:
void performLogin(BuildContext context) {
final String userName = _userName.text.trim();
final String password = _password.text.trim();
UserService.get().loginUser(userName, password).then((val) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => MainLayout()));
}).catchError((e) {
// omitted
});
}
it is passed with the widget:
class PrimaryButton extends StatefulWidget {
final bool isPrimary;
final String label;
final Function(BuildContext context) onTab;
PrimaryButton(this.label, this.isPrimary, this.onTab);
#override
State<StatefulWidget> createState() => PrimaryButtonState();
}
My main concern is, that the given onTap method should not know it is "bound" to a UI widget and therefore should not setState. Also, as this is a general button implementation I like it to be interchangeable (therefore, onTap is not hardcoded)
It looks like your problem is because you are calling setState() twice in your onTap() function. Since onTap() is not an async function it will set _apiCall = true in the first setState, then immediately run widget.onTab(context) and then immediately perform the second setState() to set _apiCall = false so you never see the loading widget.
To fix this you will need to make your onTab function an async function and await for a value in your onTap function for your InkWell:
onTap: () async {
setState(() {
_apiCall = true;
});
await widget.onTab(context);
setState(() {
_apiCall = false;
});
}
This will also let you use the results of your onTab function to show errors or other functionality if needed.
If you are unsure how to use async functions and futures here is a good guide on it that goes over this exact kind of use case.