How to keep changing a Images every 5 seconds in Flutter? - asynchronous

State Variables :
var moviePhotos = [
"http://www.kiwithebeauty.com/wp-content/uploads/2017/11/BLACK-PANTHER-COLLAGE-KIWI-THE-BEAUTY-MOVIE-MARVEL-800x350.png",
"https://static-ssl.businessinsider.com/image/5a7085a97e7a35f10c8b479f-1000/blackpanthershuri.jpg",
"https://longreadsblog.files.wordpress.com/2018/02/black-panther.jpg?w=1680",
"https://uziiw38pmyg1ai60732c4011-wpengine.netdna-ssl.com/wp-content/dropzone/2018/02/black-panther.jpg",
"https://static2.srcdn.com/wp-content/uploads/2017/10/Black-Panther-Trailer-1.jpg?q=50&w=1000&h=500&fit=crop&dpr=1.5",
"https://cdn.guidingtech.com/imager/media/assets/BP-2_acdb3e4bb37d0e3bcc26c97591d3dd6b.jpg",
"https://cdn.guidingtech.com/imager/media/assets/BP-8_acdb3e4bb37d0e3bcc26c97591d3dd6b.jpg"
];
var bannerPosition = 0;
I want the below function to change the position in the array every 5 seconds by incrementation bannerPosition so that a new image renders on the app
testing() async {
while(true){
await new Future.delayed(const Duration(seconds : 5));
if (bannerPosition < moviePhotos.length){
print("Banner Position Pre");
print(bannerPosition);
setState(() {
bannerPosition = bannerPosition + 1;
});
print("Banner Position Post");
print(bannerPosition);
}
else{
setState(() {
bannerPosition = 0;
});
}
}
}
The "Future.delayed(const Duration(seconds : 5))" does not occur in an orderly fashion when I execute this code and it results in image rendering issues.

I don't know what you mean by 'does not occur in an orderly fashion'. While just looking at that I'd think it would work, except that I seem to remember there being something weird about using await in a loop. It might keep looping around and creating more and more calls to the delayed....
Instead, use a Timer. That way it handles the looping. I'd also advise saving a reference to the timer and stopping it in your state's dispose() function.
Here's a code example:
class ImageRotater extends StatefulWidget {
List<String> photos;
ImageRotater(this.photos);
#override
State<StatefulWidget> createState() => new ImageRotaterState();
}
class ImageRotaterState extends State<ImageRotater> {
int _pos = 0;
Timer _timer;
#override
void initState() {
_timer = Timer.periodic(new Duration(seconds: 5), () {
setState(() {
_pos = (_pos + 1) % widget.photos.length;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return new Image.network(
widget.photos[_pos],
gaplessPlayback: true,
);
}
#override
void dispose() {
_timer.cancel();
_timer = null;
super.dispose();
}
}
Note that there still might be some inconsistency the first time it goes through the photos because it is just loading them as it goes. The 'gaplessPlayback' flag should make the previous image stick around until the new one is fully loaded.

Improving on "rmtmckenzie" answer, you need to use Timer.periodic if you want to repeat this every 5 seconds. See below
#override
void initState() {
_timer = Timer.periodic(Duration(seconds: 5), (Timer t) {
setState(() {
_pos = (_pos + 1) % widget.photos.length;
});
});
super.initState();
}

Related

Flutter - Admob - Display banner just on one page

I want to display my adBanner only on one page, nothing else.
Currently, when I click on my page and I go back instantly, my ad loaded, and displayed on my main page.
I partialy fix this with:
Second screen
#override
void initState() {
super.initState();
FirebaseAdMobService().showBannerAd();
}
#override
dispose() async {
FirebaseAdMobService().hideBannerAd();
super.dispose();
}
AdmobService
import 'package:firebase_admob/firebase_admob.dart';
import 'package:flutter/foundation.dart';
class FirebaseAdMobService {
static final FirebaseAdMobService _singleton = FirebaseAdMobService._internal();
FirebaseAdMobService._internal();
factory FirebaseAdMobService() {
return _singleton;
}
BannerAd _adBanner;
bool _hide = false;
init() async {
await FirebaseAdMob.instance.initialize(appId: "...");
}
static const MobileAdTargetingInfo targetingInfo = MobileAdTargetingInfo(
keywords: <String>['foo', 'bar'],
childDirected: true,
nonPersonalizedAds: true,
);
BannerAd _createBannerAd() {
return BannerAd(
adUnitId: kReleaseMode ? "..." : BannerAd.testAdUnitId,
size: AdSize.banner,
targetingInfo: targetingInfo,
listener: (MobileAdEvent event) {
print("BannerAd event $event");
},
);
}
void showBannerAd() {
_hide = false;
Future.delayed(const Duration(seconds: 2), () {
if (_hide) {
return;
}
if (_adBanner == null) {
_adBanner = _createBannerAd();
}
_adBanner.load().then((loaded) {
_adBanner.show(anchorType: AnchorType.bottom);
_hide = false;
});
});
}
void hideBannerAd() async {
_hide = true;
await _adBanner?.dispose();
_adBanner = null;
}
}
With this, my ad is displayed after 2 seconds, but if you time the ad display (press back just after 2 seconds), the ad will displayed on the main page... and with my UI, block my bottom tab control.
How to prevent my ad from appearing elsewhere than on my second screen?
Thanks

Wait for stream inside a Future: Flutter

I want to check if the Firebase DB is connected or not, so I have to use a Future to return a boolean
Have a check at my code..
#override
Future<bool> isAvailable() async {
bool ret = false;
await firebaseInstance.reference().child('.info/connected').onValue.listen((event) {
ret = event.snapshot.value;
});
return ret;
}
the firebaseInstace.reference is a StreamSubscription type and does not wait for the future to return me a result.
please help.
If you only need to know the current value, use once().then instead of onValue.listen
#override
Future<bool> isAvailable() async {
var snapshot = await firebaseInstance.reference().child('.info/connected').once();
return snapshot.value;
}
Instead of awaiting the end of the stream subscription (it never ends), just take the first value:
#override
Future<bool> isAvailable() => firebaseInstance.reference().child('.info/connected').onValue.first;
You can put the StreamSubcription in a variable
StreamSubscription subscription = someDOMElement.onSubmit.listen((data) {
// you code here
if (someCondition == true) {
subscription.cancel();
}
});
More can be found here is there any way to cancel a dart Future?
You can do the following:
#override
Future<bool> isAvailable() async {
bool ret = false;
Stream<Event> events =
FirebaseDatabase.instance.reference().child('.info/connected').onValue;
await for (var value in events) {
ret = value.snapshot.value;
}
return ret;
}
onValue returns a Stream<Event> and then you can use await for to iterate inside the Stream and get the data, and then it will return.

Getting output as "Instance of 'Future<dynamic>'" in flutter

I am trying get sum of a column in sqlite table using Bloc pattern.
debt_bloc.dart
getTotalAmount() async {
return await _debtRepository.getTotalAmt();
}
debt_dao.dart
Future<int> getTotalAmount() async {
final db = await dbProvider.database;
var result = await db.rawQuery("SELECT SUM(amount) FROM $debtDetailsTable");
int value = result[0]["SUM(amount)"];
return value;
}
debt_repositary.dart
Future getTotalAmount() => debtDao.getTotalAmount();
When i try to print like below
var total;
#override
void initState () {
super.initState();
_asyncMethod();
}
_asyncMethod() async {
var t = await debtBloc.getTotalAmount();
setState(() {
total = t;
});
}
print(total);
Output not updating when add new data. But if go back to home screen and come to respective screen value is updating.
Please guide me in right way. Thanks in advance
Await on your method first before printing it.
var total = await debtBloc.getTotalAmount(); // await here
print(total); // should now print some int value

Flutter + Firebase: How to set state on bool value in documents

I am trying to set the value of a variable based on the return value of a field (bool) in Firestore.
So far, this is what I have come up with;
First I call the method here;
#override
void initState() {
super.initState();
getAdventureStatus();
}
And this is the method.
Future getAdventureStatus() async {
Firestore.instance
.collection('adventures')
.document(widget.currentUser.id)
.collection('user_adventures')
.where('adventure_active', isEqualTo: 'false');
setState(() {
adventureActive = true;
print('${adventureActive.toString()}');
});}
What am I doing wrong and what is the most pragmatic way of doing this?
I think you just remove the set state call and set the bool to true direct.
Future getAdventureStatus() async {
Firebase.instance.document()
...
adventureActive = true;
}

How to show 5 min unstoppable timer in flutter app?

I want to show a 5 min timer in my app which does't stop even when the app is closed and show the time left in mm:ss format. How can I show the time?
Here's a very very rudimentary example of how such a timer could work which persistently stores the target time:
class TimerApp extends StatefulWidget {
#override
_TimerAppState createState() => _TimerAppState();
}
class _TimerAppState extends State<TimerApp> {
SharedPreferences prefs;
DateTime target;
String timeLeft = "";
bool running = true;
#override
void initState() async {
super.initState();
prefs = await SharedPreferences.getInstance();
target = DateTime.fromMillisecondsSinceEpoch(prefs.getInt('target'));
if (target == null || target < DateTime.now()) {
target = DateTime.now().add(Duration(minutes: 5));
}
executeTimer();
}
#override
void dispose() {
prefs.setInt('target', target.millisecondsSinceEpoch);
running = false;
super.dispose();
}
void executeTimer() async {
while (running) {
setState(() {
timeLeft = DateTime.now().isAfter(target)
? '5 min expired. Restart app to reset.'
: target.difference(DateTime.now()).toString();
});
await Future.delayed(Duration(seconds: 1), () {});
}
}
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: Text(timeLeft),
);
}
}
Note that this example is not very fleshed out; several features are missing and using a while (running) loop is probably not the most elegant solution.
Here are some more resources you could have a look at:
The shared_preferences package for saving state persistently.
This Fluttery egg timer tutorial that uses a more sophisticated form of state management.

Resources