Future Builder not building - flutter

I'm trying to include FutureBuilder but it goes into the CircularProgressIndicator() and doesn't load the actual screen code after the value of 'time' is populated by calling from SharedPreferences and the ConnectionState is done. It just gets stuck in the CircularProgressIndicator().
What am I missing here?
Future<int> getTime() async {
await MySharedPreferences.instance.getIntValue("time_key").then((value) =>
setState(() {
time= value;
}));
return time;
#override
void initState() {
super.initState();
MySharedPreferences.instance
.getStringValue("title_key")
.then((value) => setState(() {
title = value;
}));
controller =
AnimationController(vsync: this,
duration: Duration(
seconds: time));
controller2 =
AnimationController(vsync: this,
duration: Duration(
seconds: time));
controller3 =
AnimationController(vsync: this,
duration: Duration(
seconds: 1));
....}
#override
Widget build(BuildContext context){
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
backgroundColor: Colors.black,
body: FutureBuilder<int>
(
future: getTime(),
builder: ( BuildContext context, AsyncSnapshot<int> snapshot) {
print(snapshot);
print(time);
if (snapshot.connectionState == ConnectionState.done) {
print(time);
return SafeArea(
minimum: const EdgeInsets.all(20.0),
child: Stack(
children: <Widget>[
Container(
child:
Align(
alignment: FractionalOffset.topCenter,
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
height: MediaQuery
.of(context)
.size
.height / 2,
width: MediaQuery
.of(context)
.size
.height / 2,
decoration: BoxDecoration(
//shape: BoxShape.rectangle,
color: Colors.black,
image: DecorationImage(
image: AssetImage(
"assets/images/moon.png"),
fit: BoxFit.fill,
)
),
),
),
),
),
build_animation(),
],
),
);
}
else {
return CircularProgressIndicator();
}
}
),
),
);
}
build_animation() {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.all(0.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Align(
alignment: FractionalOffset.bottomCenter,
child: AspectRatio(
aspectRatio: 1.0,
child: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top:MediaQuery.of(context).size.height / 6),
child: Column(
children: <Widget>[
Text(
title.toString(),
style: TextStyle(
fontSize: 20.0,
color: Colors.black,fontWeight: FontWeight.bold,),
),
new Container(
child: new Center(
child: new Countdown(
animation: new StepTween(
begin: time,
end: 0,
).animate(controller),
.....

For starters, you do not need to setState for the result of the Future you use with a FutureBuilder. The whole point of the FutureBuilder class is to handle that for you. Also, it's best to not mix .then() and await until you have more experience. They work well together, but concentrate at one concept at a time while you are still learning.
This is your method after it's trimmed down (your choice if that's still worth a method, or if your want to put that code into iniState directly):
Future<int> getTime() async {
final value = await MySharedPreferences.instance.getIntValue("time_key");
return value;
}
You should not give that method to your FutureBuilder, otherwise you will start it anew every time build is called for any reason.
So you initState should look like this:
Future<int> futureIntFromPreferences;
#override
void initState() {
super.initState();
futureIntFromPreferences = getTime();
}
Then you can use that in your FutureBuilder:
body: FutureBuilder<int>(
future: futureIntFromPreferences,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
For a detailed explanation, read What is a Future and how do I use it?

Related

await http.get(url) does not work even in StatefulWidget

2 days ago, I asked the solution of my problem in this post and got answer to make another statefulwidget and use http codes. So my code became :
class _post_openState extends State<post_open> {
void initState() {
super.initState();
// Enable virtual display.
if (Platform.isAndroid) WebView.platform = AndroidWebView();
// else if(Platform.isIOS)WebView.platform =
}
#override
Future <String> _scrap_title(url_received) async
{
var elements;
url_received = url_received.split(' ')[-1];
if (url_received.split(':')[0] !='https')
url_received = 'https://' + url_received;
print('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
print(url_received);
bool suc = false;
// Future.delayed(Duration.zero,() async {
print('response init');
if (url_received == '' || url_received == ' ') return 'No reference';
final url = Uri.parse(url_received);
print('response parsed');
final resp = await http.get(url);
print('response on');
print(resp.statusCode);
if (resp.statusCode == 200) {
dom.Document doc = parser.parse(resp.body);
elements = doc.querySelector('title');
suc = true;
}
//}
//);
return Future.value(elements!.text);
}
Widget build(BuildContext context){
String reference = widget.data_received[5];
bool leftright = widget.data_received[0];
String content = widget.data_received[1];
String writer = widget.data_received[2];
int like = widget.data_received[3];
int shared = widget.data_received[4];
print(widget.data_received);
print('**************************************');
void Function()? _showWeb(String title, String url)
{
showDialog(
context: context,
builder: (context) {
return Container(
height: MediaQuery.of(context).size.height*0.9,
width: MediaQuery.of(context).size.width*0.95,
padding: EdgeInsets.symmetric(horizontal: 3),
//
child:
Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)
), //this right here
child:
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.symmetric(vertical: 10)),
Text(title),
Padding(padding: EdgeInsets.symmetric(vertical: 3)),
Container(height:2,color:Colors.brown),
Container(
height: MediaQuery.of(context).size.height*0.7,
width: MediaQuery.of(context).size.width*0.9,
padding: EdgeInsets.symmetric(horizontal: 5),
child:
Expanded(
child:WebView(
initialUrl: url,
javascriptMode: JavascriptMode.unrestricted,
),
),
),
Container(height:2,color:Colors.brown),
Padding(padding: EdgeInsets.symmetric(vertical: 3)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton.icon(onPressed: (){
setState(){
};
}, icon:Icon(Icons.thumb_up_alt_outlined),label: Text('like'),style: TextButton.styleFrom(primary: Colors.brown)),
GestureDetector(
onTap: (){
Navigator.pop(context);
},
child:Container
(
width: MediaQuery.of(context).size.width*0.4,
height: 30,
color:Colors.amber,
child:Text('Back',style: TextStyle(fontSize:20,color: Colors.white),
textAlign: TextAlign.center,)
),
),
TextButton(onPressed: (){
setState(){
};
}, child: Icon(Icons.share),style: TextButton.styleFrom(primary: Colors.brown)),
],
),
],
)
)
);
});
}
return
Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 1.0,
),
body:
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.topLeft,
child: SizedBox(
height: 40,
width: 40,
child:
TextButton(onPressed: () {
Navigator.pop(context);
},
style: TextButton.styleFrom(primary: Colors.brown,),
child: Icon(Icons.arrow_back)
),
),
),
Padding(padding: EdgeInsets.symmetric(vertical: 5)),
FutureBuilder<String>(
future: _scrap_title(reference),
builder: (BuildContext context, AsyncSnapshot snapshot){
print('snapshot status'+ snapshot.hasData.toString());
if (snapshot.hasData == false) {
return CircularProgressIndicator();
}
else
return GestureDetector(onTap: (){
_showWeb(writer, reference);
},
child:Text(snapshot.data,style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),),
);
}),
Padding(padding: EdgeInsets.symmetric(vertical: 5)),
Container(
height:1,
color: Colors.brown,
),
Padding(padding: EdgeInsets.symmetric(vertical: 5)),
Text(content,style: TextStyle(color:Colors.black87,fontSize: 15)),
]
)
);
}
}
But strangely, testing by print told me that this class does not even call _scrap_title.
I am having confusion of understanding lifecysle of widget/future/async and so on. Why do I keep failing to scrap html title of a web ? PLEASE somebody give me solution. Thank you so much.
Can you try replacing #override
///#override remove it
Future <String> _scrap_title(url_received) async
{....}
#override //add it here
Widget build(BuildContext context){
also better practice put variable outside the build method, you can use initState or use late
late String reference = widget.data_received[5];
late bool leftright = widget.data_received[0];
....
#override
Widget build(BuildContext context){
More about StatefulWidget

How to add button on top of image in flutter?

I'm trying to load image from network and display it fully along with button on top of the image. To achieve this I looked up on various solution and found that this can be done using Stack widget. My implementation is as below
class DisplayImage extends StatefulWidget {
final String text;
DisplayImage({required this.text}) ;
#override
State<DisplayImage> createState() => _DisplayImageState();
}
class _DisplayImageState extends State<DisplayImage> {
#override
initState() {
// TODO: implement initState
_asyncMethod();
super.initState();
}
_asyncMethod() async {
Image.network(widget.text);
setState(() {
dataLoaded = true;
});
}
bool dataLoaded = false;
#override
Widget build(BuildContext context) {
if (dataLoaded){
return Scaffold(
backgroundColor: Colors.lightBlueAccent,
appBar: AppBar(title: Text("Selfie BGchanger"),centerTitle: true,
),
body: Center(child: Stack(
children: [Image.network(
widget.text,
fit: BoxFit.fill,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
),
const SizedBox(height: 50,),
Align(
alignment: Alignment(0, .2),
child: ElevatedButton(child: const Text('Save',style: TextStyle(fontWeight: FontWeight.normal)),style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
primary: Colors.black,
// padding: EdgeInsets.symmetric(horizontal: 50, vertical: 20),
textStyle: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold)),
onPressed: () async{
String url = widget.text;
var imageId = await ImageDownloader.downloadImage(url);
if(imageId == null)
{return;}
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Saved to gallery!')));
Fluttertoast.showToast(msg: "Image saved to Gallery");
},
),
),
],),
),
);
} else {
return CircularProgressIndicator(backgroundColor: Colors.cyan,strokeWidth: 5,);
}
}
}
with this I get image is as below
save button is on top but what I'm trying to get is as below
Expected:
full sized image with save button on bottom center
I tried using boxfit.cover with height and width as infinit as below
fit: BoxFit.cover,
// height: double.infinity,
// width: double.infinity,
I got display error
How can I fix this to get expected image ? any help or suggestion on this will be highly appreciated
update:
based on answer suggestion I modified code as above and get output as below
Wrap your ElevatedButton widget with Positioned/Align widget.
Align(
alignment: Alignment(0, .2), //adjust based on your need
child: ElevatedButton(
Also you find more about Stack , Align widget.
body: Stack(
children: <Widget>[
Positioned.fill(
child: Image.network(
"",
fit: BoxFit.cover,
)),
Align(
alignment: Alignment(0, .2), // change .2 based on your need
child: ElevatedButton(
onPressed: () async {
await showDatePicker(
context: context,
initialEntryMode: DatePickerEntryMode.inputOnly,
initialDate: DateTime.now(),
firstDate: DateTime.now().subtract(Duration(days: 33)),
lastDate: DateTime.now().add(Duration(days: 33)),
);
},
child: Text("Dialog"),
),
),
],
),

Using a CachedVideoPlayer in a listview

I am attempting to show videos in a listview that is preventing me from declaring the videocontroller in the initState. This causes me to accidentally be redrawing the video multiple times during the application. I am receiving this error:
FATAL EXCEPTION: ExoPlayerImplInternal:Handler
then
java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
with my current implementation. It appears to work fora while but the memory slowly builds up until it is full. How can I implement this differently?
here is the code I am calling in the stream:
Widget getVideoItem(DocumentSnapshot doc) {
if (watchList.contains(doc['user'])) watched = true;
DateTime dateTime = DateTime.parse(doc['time']);
_videoPlayerController = CachedVideoPlayerController.network(doc["downUrl"])
..initialize();
_videoPlayerController.setLooping(true);
_videoPlayerController.play();
volumeOn = sharedPreferences.getBool("vidVol");
if (volumeOn == null) {
sharedPreferences.setBool("vidVol", false);
volumeOn = false;
}
if (volumeOn) {
_videoPlayerController.setVolume(1.0);
} else {
_videoPlayerController.setVolume(0.0);
}
return new FutureBuilder(
future: getUserData(doc["user"]),
builder: (BuildContext context, snapshot) {
return SizedBox(
height: MediaQuery.of(context).size.width + 140,
width: MediaQuery.of(context).size.width,
child: Column(children: <Widget>[
new ListTile(
title: new Text(userInfo),
subtitle: new Text(doc["title"]),
leading: FutureBuilder(
future: getProfUrl(doc),
builder: (BuildContext context, snapshot) {
Widget child;
if (!snapshot.hasData) {
child = _showCircularProgress();
} else {
child = child = new Container(
width: 44.0,
height: 44.0,
child: CachedNetworkImage(
imageUrl: doc["profUrl"],
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
);
}
return child;
}),
),
new Padding(
padding: EdgeInsets.fromLTRB(4, 4, 4, 4),
child: FutureBuilder(
future: getDownUrl(doc),
builder: (BuildContext context, snapshot) {
List<Widget> children;
if (!snapshot.hasData) {
children = [_showCircularProgress()];
} else {
children = [
Center(
child: new AspectRatio(
aspectRatio: 1 / 1,
child: Stack(
children: [
VisibilityDetector(
key: Key("unique key"),
onVisibilityChanged: (VisibilityInfo info) {
if (info.visibleFraction > .20) {
_videoPlayerController.pause();
} else {
_videoPlayerController.play();
}
},
child: CachedVideoPlayer(
_videoPlayerController,
)),
IconButton(
icon: volumeOn
? Icon(Icons.volume_up)
: Icon(Icons.volume_off),
onPressed: () {
setState(() {
_videoPlayerController.pause();
sharedPreferences.setBool(
"vidVol", !volumeOn);
});
},
),
],
),
),
)
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
);
}),
),
new Row(
children: [
new IconButton(
icon: !watched
? new Icon(
Icons.remove_red_eye,
color: Colors.black26,
)
: new Icon(
Icons.remove_red_eye,
color: Colors.blueGrey[400],
),
onPressed: () {
initToggleWatched(watchList, doc["user"], name, position,
secPosition, state, year, user);
}),
Padding(
padding: EdgeInsets.fromLTRB(5, 0, 0, 0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
dateTime.day.toString() +
"/" +
dateTime.month.toString() +
"/" +
dateTime.year.toString(),
style: TextStyle(color: Colors.black26, fontSize: 12),
),
),
),
],
)
]),
);
},
);
}
Try making the widget with a controller a separate StatefullWidget instead of putting everything in one place and manage the instantiation and disposal of the controller in the initState() and dispose() methods.

Flutter reload Ui after refreshing FutureBuilder

in this my code, when application couldn't get data from web server, that can be show reload button, after that when i click on reload button, my method can be get data from web server again, my problem is after this action i can't reload UI with this data and reload button is shown always
class _LessonDetailState extends BaseState<LessonDetail> {
String monthKey;
String lessonFileKey;
int monthId;
_LessonDetailState(this.monthKey, this.lessonFileKey,this.monthId);
Future<PlayLessonResponse> _myResponse;
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = new GlobalKey<RefreshIndicatorState>();
#override
void initState() {
Future.delayed(Duration.zero,() {
_myResponse = _getLessonDetail(context, monthKey, lessonFileKey);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: FutureBuilder<PlayLessonResponse>(
future: _myResponse,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if(snapshot.hasData){
//...
}else{
return RefreshIndicator(
key: _refreshIndicatorKey,
child: Container(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(15),
child: Text(
'Error',
style: AppTheme.of(context).caption(),
),
),
RaisedButton(
color: Colors.white,
child: Text(
'Reload',
style: AppTheme.of(context).caption(),
),
onPressed: (){
return _getLessonDetail(context, monthKey, lessonFileKey);
},
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0))),
],
),
),
onRefresh: (){
return _getLessonDetail(context, monthKey, lessonFileKey);
},
);
}
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
Future<PlayLessonResponse> _getLessonDetail(BuildContext context, String monthKey, String lessonFileKey) async {
try{
//...
return PlayLessonResponse.fromJson(response.body);
}catch(error){
print(error);
return null;
}
}
}
In your onRefresh's _getLessonDetail() function, you should call setState() to rebuild the widget
setState(() {
_myResponse = _getLessonDetail(context, monthKey, lessonFileKey);
});

How to hit an API when time is 00:00 in Flutter?

I am working on my first Flutter App. There is an Activity/Screen which is showing a countdown time. I want, when time is 00:00 then hit an API.
I tried and search on it but i didn't find anything best about this problem.
Please tell me how to resolve this ?
Thank you.
String get timerString {
Duration duration = controller.duration * controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}';
}
String time;
String topicName;
Future<String> getTime() async{
final response = await http.post(Constant.TestTime,
body: {
"topic_id" : widget.valueTopicId,
});
Map<String,dynamic> respons = jsonDecode(response.body);
setState(() {
time=respons['test_time'];
topicName=respons['topic_name'];
controller = AnimationController(
vsync: this,
duration: Duration(minutes:int.parse(time) ,seconds: 00),
);
if (controller.isAnimating)
controller.stop();
else {
controller.reverse(
from: controller.value == 0.0
? 1.0
: controller.value);
}
});
print(respons.toString());
setState(() {
isLoading=false;
});
}
Widget _buildAppBar (BuildContext context){
return PreferredSize (preferredSize: Size.fromHeight(MediaQuery.of(context).size.height * 0.085),
child: AppBar(
backgroundColor: Colors.blue,
title: Text("Questions"),
centerTitle: true,
iconTheme: IconThemeData(
color: Colors.white
),
actions: <Widget>[
new Container(
margin: const EdgeInsets.only(top: 25.0, left: 25.0),
child: new Text(topicName,
style: TextStyle(color: Colors.white, fontSize: 26,
fontWeight: FontWeight.bold),),
),
SizedBox(width: 20),
new Padding(padding: EdgeInsets.all(20.0)),
new Padding(padding: EdgeInsets.only(bottom: 40, top: 50)),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Align(
alignment: FractionalOffset.center,
child: AspectRatio(aspectRatio: 1.0,
child: Stack(
children: <Widget>[
Positioned.fill(child:
AnimatedBuilder(
animation: controller,
builder: (BuildContext context,
Widget child) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: CustomPaint(
painter: TimerPainter(
animation: controller,
backgroundColor: Colors.white,
color: Colors.blue,
),
),
);
},
),
),
Align(
alignment: FractionalOffset.center,
child: Column(
mainAxisAlignment: MainAxisAlignment
.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment
.center,
children: <Widget>[
AnimatedBuilder(
animation: controller,
builder: (BuildContext context,
Widget child) {
return Text(
timerString, style: TextStyle(
color: Colors.white),
);
}
)
],
),
)
],
),
)
),
),
],
)
)
],
) ,);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: onBackPressed,
child: isLoading
? Center(child: CircularProgressIndicator(),)
:MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
resizeToAvoidBottomPadding: false,
appBar: _buildAppBar(context),
body: Questions(widget.valueTopicId,widget.noOfQuestions,widget.difficulty,time),
)
),
);
}
}
Here i posted my code and an API getTime() is hitting on initState(). So, when my time is 00:00 then an another API should hit. I tried with FutureBuilder but it did not work. May be I did something wrong but so confusing in this.
You can user Timer, you need to import dart:async
Timer(Duration(seconds: 5), () {
getTime();
});
You, need to register your timer in initState.
Timer.periodic() will be executed at a particular duration after specified time.
Timer _timer;
#override
void initState()
{
_timer = Timer.periodic(
const Duration(seconds: 1), (Timer t) => handleAPICall(),
);
super.initState();
}
#override
void dispose()
{
_timer?.cancel();
super.dispose();
}
void handleAPICall()
{
// here you need to check if its time to make the API call.
// I am assuming that you will have some datetime that will tell you
// if duration time is 00:00
if(duration_time == 00:00) // you need to write your correct logic
{
// then, make the HTTP Call.
}
}