I am using Futurebuilder in flutter and having issue while closing the showDialog
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size( 50.0),
body: FutureBuilder(
future: widget.getAutoCompleteData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
Navigator.of(context).pop();
} else if (snapshot.hasError) {
} else {
LoadingDialog.showLoadingDialog(context, _scaffoldKey);
}
return Center(
child: Container(child: Text("sds"),),
);
}));
}
Getting below error when screen loads
package:flutter/src/widgets/navigator.dart': Failed assertion: line 5013 pos 12: '!_debugLocked': is not true
Change this
FutureBuilder(
future: widget.getAutoCompleteData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
Navigator.of(context).pop();
} else if (snapshot.hasError) { //here this is empty
} else {//remove else statement
LoadingDialog.showLoadingDialog(context, _scaffoldKey);
}
return Center(
child: Container(child: Text("sds"),),
);
})
To This
FutureBuilder<List<Post>>(
future: _dataFetcher.getPosts(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return LoadingDialog.showLoadingDialog(context, _scaffoldKey);
}
return Center(child: Text('${snapshot.data![0].title}'));
},
)
It may be caused by the re-entrant of the Navigator (you can check the answer here: Error thrown on navigator pop until : “!_debugLocked': is not true.”
)
Or, maybe you don't want to use FutureBuilder. The FutureBuilder is meant to stay in the same widget/page and show different screens when future data is not ready. If you want to push a loading dialog and close it when data is ready, you can just simply use a Future function
Future pressTheButton(context) async {
LoadingDialog.showLoadingDialog(context, _scaffoldKey); // showDialog here
final data = await getAutoCompleteData(); // await the data
Navigator.of(context).pop(); // pop the loading dialog
// return your data or error
// or rebuild the current widget with the data
}
Related
I have a little problem here where i have logged in with Google Auth using Firebase but everytime i tried to restart the app i expect the app will show the HomePage() without any problem, but i found that before it return, the app had like a bit seconds in LoginPage() before displaying HomePage(), is there any way to make it seamlessly
class AuthService extends StatelessWidget {
const AuthService({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return HomePage();
} else {
return LoginPage();
}
},
),
);
}
}
It is happening because for snapshot to reach snapshot.hasData state it takes time, and meanwhile else part is executed which is LoginPage().
How to overcome this?
Try to wrap within snapshot.connectionState == ConnectionState.active which means once stream is connected then check the condition else return CircularProgressIndicator
Code:
StreamBuilder(
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
return HomePage();
} else {
return LoginPage();
}
}
return const CircularProgressIndicator();
},
);
Share() must display a modal in order to let the user wait in front of a circular progress indicator while I am loading the video file URL.
My code is as below, but I am puzzled about how to architecture: I need to trigger the sharing only once the snapshot.hasData.
How can that be done?
Btw, I use share_plus
Future<void> share(BuildContext context) async {
showModalBottomSheet(
context: context,
builder: (context) {
return FutureBuilder(
future: Video.videoUrl(videoUrl!),
builder: (context, snapshot) {
final file = XFile(snapshot.data!);
Share.shareXFiles([file],
text: "Don't miss this out! Only on Shokaze");
return SizedBox(
height: 200,
child: Center(
child: !snapshot.hasData
? Column(children: [
Text("Preparing sharing…"),
const CircularProgressIndicator(),
])
: Text("Sharing…")));
});
});
}
You should refactor the FutureBuilder using if/else conditions:
if (snapshot.hasData) {
Share.share(snapshot.data!);
return Text(snapshot.data.toString());
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else {
return Scaffold(body: Center(child: CircularProgressIndicator()));
}
Future<void> share(BuildContext context) async {
showModalBottomSheet(
context: context,
builder: (context) {
return FutureBuilder(
future: myFuture(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
Share.share(snapshot.data!);
return Text(snapshot.data.toString());
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else {
return Scaffold(body: Center(child: CircularProgressIndicator()));
}
},
);
},
);
}
final String apiUrl = 'https://api.covid19api.com/summary';
Future globaldata() async{
var res = await http.get(Uri.parse(apiUrl));
Map s = await jsonDecode(res.body);
return s['Global']['NewConfirmed'];
}
//....
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("global cases"),
centerTitle: true,
),
body:Container(
child: FutureBuilder(
future: globaldata(),
builder: (context, snapshot){
if(snapshot.data != null){
print(globaldata());
return Text(globaldata().toString());
}else{
return Text("loading ...");
}
},
),
),
);
}
}
I am getting error of Instance of 'future'
Try doing these
child: FutureBuilder(
future: globaldata(),
builder: (context, snapshot) {
if(snapshot.data != null){
print(snapshot.data);
return Text(snapshot.data);
}else{
return Text("loading ...");
}
},
),
),
);
Check Print and return Text statement
You get this error because you are printing and returning Futures without await:
print(globaldata());
return Text(globaldata().toString());
Your FutureBuilder provides you the values you are trying to access in snapshot:
print(snapshot.data);
return Text(snapshot.data.toString());
Change your function to this.
Future globaldata() async {
http.Response response = await http.get(Uri.parse(apiUrl));
if (response.statusCode == 200) {
return jsonDecode(response.body)['Global']['NewConfirmed'];
} else {
throw Exception('Failed to load post');
}
}
and change your body to this.
body: FutureBuilder(
future: globaldata(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: Text(snapshot.data.toString()),
);
} else if (snapshot.hasError) {
return Center(
child: Text("${snapshot.error}"),
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
This works perfectly.
I just started writing with Flutter.I am constantly getting this error. What should i do? Here is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<WeatherInfo>(
future: futureWeather,
builder: (context, snapshot) {
if (snapshot.hasData) {
} else if (snapshot.hasError) {
return Center(
child: Text("${snapshot.error}"),
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<WeatherInfo>(
future: futureWeather,
builder: (context, snapshot) {
if (snapshot.hasData) {
} else if (snapshot.hasError) {
return Center(
child: Text("${snapshot.error}"),
);
}
return const Center(child: CircularProgressIndicator());
}));
}
Inside the FutureBuilder you covered the case in which you have the data or you have an error, but not when you are expecting for the future to complete(And don't have neither the data or an error).
I just added a Circular progress indicator to be shown while no data or no error are returned from the Future, that should prevent the FutureBuilder from returning null. And when the snapshot state changes the data or error would be shown.
I think you should try and wrap the future builder in a container
I have a stream builder for loading a list of video posts. Whenever an error occurs, I add an error to the sink and the StreamBuilder displays an error message with a "Try Again" button. My issue is that when I press the button, Nothing (visually) happens but I want to replace the error message with a spinningLoader while it's retrying.
How would I achieve this?
I was thinking of adding a boolean to the sink and check if snapshot.data is bool and return the spinningLoader if it is, but this seems counter-intuitive.
#override
Widget build(BuildContext context) {
return SafeArea(
child: Container(
color: backgroundColor,
child: StreamBuilder(
stream: _streamController.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return buildRelatedList(snapshot.data);
} else if (snapshot.hasError) {
return retryButton();
} else {
return _spinningLoader;
}
},
),
),
);
}
_fetchVideos() async {
List videos = await RelatedVideos.fetchVideos(
id: widget.id);
if (videos.isEmpty) {
_streamController.sink.addError('error loading');
} else {
_streamController.sink.add(videos);
}
}
You can use ConnectionState:
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.none: return Text('Select lot');
case ConnectionState.waiting: return Text('Awaiting bids...');
case ConnectionState.active: return Text('\$${snapshot.data}');
case ConnectionState.done: return Text('\$${snapshot.data} (closed)');
}
return null; // unreachable
},
)
That will return the state of connection to an asynchronous computation. Also you can change the Text widget to another widget, for example you can use CircularProgressIndicator();
https://api.flutter.dev/flutter/widgets/ConnectionState-class.html
https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html