Flutter Slider, problem with lists, map and elements inside - flutter

I try to put a Corousel Slider but with a List that is updated automatically in another part of the app.
The documentation for the slider is with static photos. I want to access to the items inside a list and iterate.
var itemss = [];
for (var e = 0; e < model.listFireforce.length; e++) {
itemss.add(model.listFireforce[e].image);
}
return CarouselSlider(
height: 350.0,
items: [ itemss // HERE IS THE PROBLEM !!
].map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 2.0),
decoration: BoxDecoration(color: Colors.white),
child:
Image.network(
i,
fit: BoxFit.cover,
));
},
);
}).toList());
i itearate only 1 time, but no for all the items inside. I try forEach() but i have no solution.
Thanks!

Your problem is that you wrap your itemss array in another array and map over that. You may want to try this:
return CarouselSlider(
height: 350.0,
items: itemss.map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 2.0),
decoration: BoxDecoration(color: Colors.white),
child: Image.network(
i,
fit: BoxFit.cover,
));
},
);
}).toList(),
);

Related

Updating list in memory doesn't change UI in Flutter

I am currently modifying my app to support large devices. In this app I want to have a list of categories at the left and when I tap the list tile I want to show the list of the items which the category holds on the left. By the approach I am using the list updates in memory but doesnt change the UI. This is my current approach ->
class _OrderCategoryScreenState extends State<OrderCategoryScreen> {
List<FoodItem> filteredLists = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: [
ValueListenableBuilder<Box<FoodCategory>>(
valueListenable: Boxes.getFoodCategories().listenable(),
builder: (context, box, child) {
final categories = box.values.toList().cast<FoodCategory>();
return categories.length == 0
? Container(
color: Color(0xFFFCF9F9),
height: double.infinity,
width: MediaQuery.of(context).size.height * 0.30,
child: Center(
child: Text("No Categories"),
))
: Container(
height: double.infinity,
width: MediaQuery.of(context).size.height * 0.35,
child: Drawer(
backgroundColor: Color(0xFFFCF9F9),
elevation: 0,
child: ListView(
children: <Widget>[
DrawerHeader(
child: ListView(
children: [
Container(
height: MediaQuery.of(context).size.height,
child: ValueListenableBuilder<
Box<FoodCategory>>(
builder: (context, value, child) {
final foodCategories = box.values
.toList()
.cast<FoodCategory>();
return ListView.builder(
itemCount: foodCategories.length,
itemBuilder: (context, index) {
return ListTile(
tileColor: Colors.green,
onTap: () {
filteredLists = Boxes
.getFoodItems()
.values
.where((element) =>
element.categoryName ==
foodCategories[index]
.name)
.toList();
print(filteredLists.length);
print("object");
},
title: Text(
foodCategories[index].name),
);
},
);
},
valueListenable: Boxes.getFoodCategories()
.listenable(),
),
)
],
),
),
],
),
));
},
),
Container(
width: MediaQuery.of(context).size.width * 0.5,
height: MediaQuery.of(context).size.height * 0.5,
color: Colors.amberAccent,
child: filteredLists.isEmpty
? Center(
child: Text("Choose A Category"),
)
: ListView.builder(itemCount: filteredLists.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(filteredLists[index].itemName),
);
},
),
),
],
)
),
);
}
}
When I hot reload using this approach it works on the UI as well.
Please Help.
Try calling setState to update the UI.
onTap: () {
filteredLists = Boxes
.getFoodItems()
.values
.where((element) =>
element.categoryName ==
foodCategories[index]
.name)
.toList();
print(filteredLists.length);
print("object");
setState((){}); //this
},

Flutter: How to use my stored images for carousel

I am using a carousel slider widget, instead of sourcing for images link, I have them in an asset folder, is there anyway I can use it for my carousel instead of images link.
class _HomePageState extends State<HomePage> {
final List<String> firstImages = [
'https://cdn.pixabay.com/photo/2020/11/01/23/22/breakfast-5705180_1280.jpg',
'https://cdn.pixabay.com/photo/2016/11/18/19/00/breads-1836411_1280.jpg',
'https://cdn.pixabay.com/photo/2019/01/14/17/25/gelato-3932596_1280.jpg',
'https://cdn.pixabay.com/photo/2017/04/04/18/07/ice-cream-2202561_1280.jpg',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
CarouselSlider.builder(
options: CarouselOptions(height: 161),
itemCount: firstImages.length,
itemBuilder: (context, index, realIndex) {
final firstImage = firstImages[index];
return buildImage(firstImage, index);
},
),
I used my Carousel slider by extracting the method
Widget buildImage(String firstImage, int index) {
return Container(
margin: EdgeInsets.all( 20),
color: Colors.grey,
child: Image.network(
firstImage,
fit: BoxFit.cover,
width: 250,
height: 50,
)
);
}
I made use of Image network widget. Is there anyway I can go about it. Thanks
Assuming you have images in the assets folder and that you have added those paths in pub spec.yaml
you can add the images in the list
final List<String> firstImages = [
'assets/images/image1.png',
'assets/images/image2.png',
'assets/images/image3.png',
'assets/images/image4.png',
];
then in build images use Image.asset
Widget buildImage(String firstImage, int index) {
return Container(
margin: EdgeInsets.all( 20),
color: Colors.grey,
child: Image.asset(
firstImage,
fit: BoxFit.cover,
width: 250,
height: 50,
)
);
}

Flutter How to make an image not show on a condition

I need an image to show or not show, on a condition and the condition is that if the answer of the user is correct than the button will show but if the answer of the question is wrong than the image will not display. the condition is if (joinedWords !=
data[widget.CurrentIndex].sentence // dont display the next
level image. if the
asnwer is correct than the button will show.
String joinedWords =
selectedWords.join(" ").toString();
String _setRatingOnAlert() {
//print(controller.duration?.inSeconds);
print(data![widget.CurrentIndex].timer);
print(data[widget.CurrentIndex].level);
print(joinedWords);
print(data[widget.CurrentIndex].sentence);
print(widget.CurrentIndex);
if (joinedWords ==
data[widget.CurrentIndex].sentence) {
starsRating = 'assets/threestars_small.png';
} else {
starsRating = 'assets/zerostars_small.png';
}
return starsRating;
}
return Container(
child: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding:
const EdgeInsets.only(right: 110),
child: FlatButton(
onPressed: () {
setState(() {
selectedWords.clear();
Navigator.pop(context);
});
},
child: Image.asset(
'assets/restartbutton.small.png',
height: 80,
width: 80,
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding:
const EdgeInsets.only(left: 110),
child: FlatButton(
onPressed: () {
Navigator.pop(context);
/// E close munu
Navigator.pop(context);
/// E hek current quiz page
Navigator.push(
/// E qel next level quiz
context,
MaterialPageRoute(
builder: (context) =>
RenditFjaletButton(
QuizList: widget
.AllQuizLists[
widget.CurrentIndex +
1]
.word,
AllQuizLists:
widget.AllQuizLists,
CurrentIndex:
widget.CurrentIndex + 1,
),
),
);
},
child: Image.asset(
'assets/next_small.png', // this is the button that i
dont want to show if the
answer is wrong
height: 80,
width: 80,
),
),
),
),
use ternary operator in this case.
bool answer = false;
//do some operation with answer;
(answer)? ShowImage():Container();
Widget showImage(){
return Container(
child: Image.network(
'imagePath',
fit: BoxFit.fitWidth,
);
}
You can use ternary operator incase you need the conditions.Do as follows:
isTrue? Image.asset():Container()

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.

List builder : how to make the first element different?

I am building a list (dynamically, throught the result of an HTTP request to an API) but I want to add a 'add' card first, before the others elements.
Like this :
I'm building this with a builder
But the way I do it is not good, there's a problem.
Here, account.trips is a list of Trip object and AddNewTrip is the 'add' card.
Because of my if, the first element of the list is always not displayed
So, is there another way to put my 'add' card as the first element of the list, without hiding one element ?
return Builder(
builder: (BuildContext context) {
if (account.trips.indexOf(i) == 0) {
return new AddNewTrip(account);
}
return Padding(
padding: const EdgeInsets.only(
top: 20.0, bottom: 40.0, left: 5.0, right: 5.0),
child: Stack(
...
EDIT : here is the full code :
Expanded(
child: ScopedModelDescendant<Account>(
builder: (context, child, _account){
print(_account.profile);
return new CarouselSlider(
enableInfiniteScroll: false,
enlargeCenterPage: true,
height: MediaQuery.of(context).size.height,
initialPage: 1,
items: itemBuilder(_account)
);
},
),
)
List<Widget> itemBuilder(Account account) {
if (account.trips != null)
return account.trips.map((i) {
return Builder(
builder: (BuildContext context) {
if (account.trips.indexOf(i) == 0) {
return new AddNewTrip(account);
}
return Padding(
padding: const EdgeInsets.only(
top: 20.0, bottom: 40.0, left: 5.0, right: 5.0),
child: Stack(
children: <Widget>[
Hero(
tag: i.id,
child: Material(
type: MaterialType.transparency,
child: InkWell(
child: Container(
height: 200,
margin: EdgeInsets.symmetric(horizontal: 5.0),
decoration: cardImage(i.imageUrl),
),
borderRadius: BorderRadius.circular(20.0),
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return TripDetails(i);
}));
},
),
),
),
Positioned(
width: MediaQuery.of(context).size.width * 0.7,
height: MediaQuery.of(context).size.height * 0.48,
top: MediaQuery.of(context).size.height * 0.2,
left: MediaQuery.of(context).size.width * 0.035,
child: Hero(
tag: i.id + 'container', //should be an ID
child: Material(
type: MaterialType.transparency,
child: Container(
decoration: cardDecoration,
child: new TripContent(i, true),
),
),
),
)
],
),
);
},
);
}).toList();
else
return [
new AddNewTrip(account)
];
}
The previous answers already provide a good general advice on how to do this.
For your specific example you would have to do something like this:
When looping over your accounts, prepend the result with your "Add" card like this:
List<Widget> itemBuilder(Account account) {
return [
new AddNewTrip(account),
...(account.trips?.map((i) {/** same as before without your index check **/}) ?? []),
];
}
return account.trips.map((i) {
change to:
return [[null], account.trips].expand((x) => x).toList().map((i) {
how about this you build the card first then build rest of your list..
List<Widget> itemBuilder(Account account) {
return [
AddNewTrip(account),
if (account.trips != null)
return account.trips.map((i) {
return Builder(
builder: (BuildContext context) {
return Padding(
//rest of your code
);
});
);
}];}
You can add a dummy list item in your list at 0th index and later you can perform your logic.
Below is sample code can help you out:
var list = ["1","3","4"];
list.insert(0,"5");
Output:5 1 3 4