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,
)
);
}
Related
I have horizontal ListView.builder and CupertinoSliverRefreshControl, so when it reaches the end, I want to display Loading indicator, but for some reason I am getting error
Null check operator used on a null value
The relevant error-causing widget was
CustomScrollView
lib/sliver_loading.dart:19
The most unclear part is that CupertinoSliverRefreshControl works fine with Vertical ListView.builder, but when I change Axis on horizontal it rises this above error.
Here is a code :
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15),
child: CustomScrollView(
scrollDirection: Axis.horizontal, // Here is when Error rise
slivers: [
SliverToBoxAdapter(
child: SizedBox(
height: 200,
child: ListView.builder(
scrollDirection: Axis.horizontal,
primary: false,
shrinkWrap: true,
itemCount: 4,
itemBuilder: (context, index) {
return Container(
width: 100,
height: 200,
color: colors[index],
);
},
),
),
),
CupertinoSliverRefreshControl(
onRefresh: () async {
await Future.delayed(Duration(seconds: 3));
print('loaded');
},
),
],
),
),
);
}
Can anyone explain me, why is this happening and what are the solutions?
There is a workaround with current snippet instead of using CupertinoSliverRefreshControl return row with loading widget for last item. Also wrap Container with Center.
itemBuilder: (context, index) {
return index == 13 // items length-1
? Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: EdgeInsets.all(20),
width: 100,
height: 200,
color: Colors.cyanAccent,
),
CircularProgressIndicator(),
],
)
:Center( child: Container(
margin: EdgeInsets.all(20),
width: 100,
height: 200,
color: Colors.amber,
));
},
If you do use ListView, you can use ScrollController with listener and get position to load data using controller.position.maxScrollExtent* .9 ;load more on 90% scroll.
Also, using the same directional multi-scrollabe widgets is not necessary. We can skip using ListView and use SliverList. While the width is fixed, we can compare the items' length and current scroll position to using the controller.
final ScrollController controller = ScrollController();
#override
void initState() {
super.initState();
controller.addListener(() {
print(controller.offset);
//14 total item , I am using 90%
if (controller.offset > 100 * 14 * .9) {
// you may encounter multiple call use another flag or null to handle this
print("load more");
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15),
child: CustomScrollView(
scrollDirection: Axis.horizontal,
controller: controller,
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => index == 13 // items length-1
? Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: EdgeInsets.all(20),
width: 100,
height: 200,
color: Colors.cyanAccent,
),
CircularProgressIndicator(),
],
)
: Center(
child: Container(
margin: EdgeInsets.all(20),
width: 100,
height: 200,
color: Colors.amber,
)),
childCount: 14,
),
),
],
),
),
);
}
}
Okay, so here is a way how I solved this problem. Since CupertinoSliverRefreshControl does not work with horizontal ListView.builder, I decided to use CupertinoActivityIndicator and CupertinoActivityIndicator.partiallyRevealed.
When ListView reaches to the end, I am calculating distance between ListView.builder() and int distance and updating double progress for CupertinoActivityIndicator.partiallyRevealed, next when progress reaches 1.0 I just replace CupertinoActivityIndicator.partiallyRevealed with CupertinoActivityIndicator changing bool isActive value to true.
Finally it works like CupertinoSliverRefreshControl, just without slivers :).
Code Example
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HorizontalLoader extends StatefulWidget {
const HorizontalLoader({Key? key}) : super(key: key);
static final colors = [
Colors.red,
Colors.indigoAccent,
Colors.purple,
Colors.amberAccent,
Colors.orange,
Colors.purple,
Colors.cyanAccent,
Colors.red,
Colors.indigoAccent,
Colors.purple,
];
#override
State<HorizontalLoader> createState() => _HorizontalLoaderState();
}
class _HorizontalLoaderState extends State<HorizontalLoader> {
int distance = 70; // offset
bool isActive = false;
double progress = 0.0;
// Base logic. you can also use this logic with ScrollController()
bool _handleNotification(ScrollNotification notify) {
double outRangeLoading = distance + notify.metrics.maxScrollExtent;
double currentPixel = notify.metrics.pixels;
if (notify.metrics.extentAfter <= 0.0) {
if (currentPixel >= outRangeLoading) {
networkLoader();
}
calculateProgress(outRangeLoading, currentPixel);
}
return true;
}
// Some math
void calculateProgress(outRangeLoading, currentPixel) {
double current, currentAsPrecent;
current = outRangeLoading - currentPixel;
currentAsPrecent = (100 * current) / distance;
setState(() {
progress = (100 - currentAsPrecent) * 0.01;
});
}
// To simulate loading data from Network
void networkLoader() async {
isActive = true;
await Future.delayed(Duration(seconds: 3));
isActive = false;
setState(() {
progress = 0.0;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 200, bottom: 200),
child: Stack(
children: [
Positioned(
right: 15,
top: 210,
child: isActive
? CupertinoActivityIndicator()
: CupertinoActivityIndicator.partiallyRevealed(
progress: progress,
),
),
NotificationListener<ScrollNotification>(
onNotification: _handleNotification,
child: ListView.builder(
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
itemCount: HorizontalLoader.colors.length + 1,
itemBuilder: (context, index) {
if (index == HorizontalLoader.colors.length) {
return isActive ? SizedBox(width: 50) : SizedBox();
}
return Container(
width: 100,
height: 100,
color: HorizontalLoader.colors[index],
);
},
),
),
],
),
),
);
}
}
This code is for picking multiple images from the gallery using Image Picker Package and showing them on the home screen. After that drag one of the image from the selected images and drop it to drop target by removing the dropped image from the list of selected images. But I'm unable to do as it is showing an exception.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class Gallery extends StatefulWidget {
const Gallery({Key? key}) : super(key: key);
#override
_GalleryState createState() => _GalleryState();
}
class _GalleryState extends State<Gallery> {
final List<XFile>? selectedImagesList = [];
final List<XFile> dropTargetList = [];
#override
Widget build(BuildContext context) {
double size = 230;
return Scaffold(
appBar: AppBar(
title: const Text('Gallery'),
),
body: Column(
children: [
ElevatedButton(
onPressed: () async {
final List<XFile>? selectedImages = await ImagePicker().pickMultiImage();
if (selectedImages!.isNotEmpty) {
setState(() {
selectedImagesList!.addAll(selectedImages);
});
}
},
child: const Text('Select Images'),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
itemCount: selectedImagesList!.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5),
itemBuilder: (BuildContext context, int index) {
return Draggable<XFile>(
child: Image.file(File(selectedImagesList![index].path),
fit: BoxFit.cover, width: 230, height: 230),
feedback: Image.file(
File(selectedImagesList![index].path),
fit: BoxFit.cover,
width: 230,
height: 230),
childWhenDragging: Container(
color: Colors.red, width: size, height: size),
);
}),
),
),
Container(
width: size,
height: size,
color: Colors.green,
child: DragTarget<XFile>(
builder: ((context, candidateData, rejectedData) {
return Row(
children: dropTargetList
.map((target) => Image.file(File(target.path),
fit: BoxFit.cover, width: size, height: size))
.toList());
}),
onWillAccept: (data) => true,
onAccept: (data) {
setState(() {
dropTargetList.add(data);
selectedImagesList
?.removeWhere((photo) => photo.path == data.path);
});
})),
const SizedBox(height: 200)
],
),
);
}
}
This is the exception while dropping the image https://i.stack.imgur.com/ParUz.png
Emulator screen getting stuck on dropping https://i.stack.imgur.com/5lEsN.png
To prevent the exception from occuring, make sure to only accept data if data is not null:
onWillAccept: (data) => data != null,
This will prevent the exception.
Looking further on why data is null, it's because you don't set it when you create your draggable:
return Draggable<XFile>(
child: ...,
...
data: selectedImagesList![index],
);
Now it will work as you expect it to. Dragging multiple images down causes some overflow, but you should be able to work on that with the exception out of your way :)
From the image below, i want to set border around the first image when the page loads and also set it as the main image. Once the next image is clicked, it has to be set as the main image with the border around it. I am trying to achieve this in flutter and the list of images were built using a ListView builder. Can anyone be of help. The heroImage widget depends on the image selection from the images widget and on page load, the heroImage must be set to the first image in the images widget.
// Selected image tile being used in the listView
class SelectedImageTile extends StatelessWidget {
final GestureTapCallback onTap;
final String imageAsset;
final BoxDecoration decoration;
SelectedImageTile({
this.onTap,
this.imageAsset,
this.decoration
});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.all(2),
decoration: decoration,
child: ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Image.asset(
imageAsset,
fit: BoxFit.cover,
height: 60,
width: 60,
),
),
),
);
}
}
Widget heroImage() {
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
'assets/image-1.png',
height: 350,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover
),
);
}
Widget images() {
return SizedBox(
height: 75,
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
final galleryImage = galleryImages[index];
return SelectedImageTile(
imageAsset: galleryImage,
onTap: () {},
decoration: BoxDecoration(
border: Border.all(color: EShopColors.primary),
borderRadius: BorderRadius.circular(8)
),
);
},
itemCount: galleryImages.length,
),
);
}
Convert images() widget to a StateFulWidget
Create a variable selectedImage and set it to URL of the image tapped (when tapped)
And finally add a condition to the border of the image tile.
class Images extends StatefulWidget {
#override
_ImagesState createState() => _ImagesState();
}
class _ImagesState extends State<Images> {
var selectedImage = galleryImages[0]
#override
Widget build(BuildContext context) {
return SizedBox(
height: 75,
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
final galleryImage = galleryImages[index];
return SelectedImageTile(
imageAsset: galleryImage,
onTap: () {
this.selectedImage = galleryImage;
setState(() {});
},
decoration: BoxDecoration(
border:galleryImage==this.selectedImage? Border.all(color: EShopColors.primary):null,
borderRadius: BorderRadius.circular(8)),
);
},
itemCount: galleryImages.length,
),
);
}
}
I am having difficulty going to the next page when I am touching the image on the interface? I trying to set a category Listview. The listview holds the image and how can I set it when the user touches the image, so they can go to the next page?
The code
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'custom_textW.dart';
class Category {
final String name;
final String image;
Category({#required this.name, #required this.image});
}
List<Category> categoriesList = [
Category(name: "Sarapan", image: "nasilemak.png"),
Category(name: "Kuih", image: "kuih.png"),
Category(name: "Makan Tengahari", image: "lunch.png"),
Category(name: "Minum Petang", image: "mnmptg.png"),
Category(name: "Makan Malam", image: "mknmlm.png"),
Category(name: "Minum Malam", image: "mnmmlm.png"),
Category(name: "Minuman", image: "air2.png"),
];
class Categories extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 109,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categoriesList.length,
itemBuilder: (_, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(color: Colors.white, boxShadow: [
BoxShadow(
color: Colors.red[200], offset: Offset(4, 6), blurRadius: 20)
]),
//tambah di sini kalau nk gesture
child: Image.asset(
"images/${categoriesList[index].image}",
width: 80,
),
),
SizedBox(
height: 5,
),
CustomText(
text: categoriesList[index].name,
size: 14,
colors: Colors.black,
)
],
),
);
},
),
);
}
}
how the listview looks like:
right now when I am trying to touch the image, nothing will happen. It just displays the image.
You can wrap the image widget inside the InkWell Widget and implement onTap() method to navigate to next page.
child: InkWell(
onTap: ()=> NavigateToPage(),
child: Image.asset(
"images/${categoriesList[index].image},
width: 80,
),
),
You can wrap your image with GestureDetector like this:
child: GestureDetector(
onTap: // go to next page,
child: Image.asset(
"images/${categoriesList[index].image}",
width: 80,
),
),
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'Login.dart';
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image:DecorationImage(
image: AssetImage("images/black_background_logo.png"),
fit: BoxFit.cover,
)
),
child: Column(
children: [
CarouselDemo(),
HomePanel()
],
),
);
}
}
List<String> images = [
'https://skalka-app.ru/banners/1.png',
'https://skalka-app.ru/banners/2.png',
'https://skalka-app.ru/banners/3.png',
] ;
class CarouselDemo extends StatelessWidget {
CarouselController buttonCarouselController = CarouselController();
#override
Widget build(BuildContext context) => CarouselSlider(
options: CarouselOptions(
height: MediaQuery.of(context).size.height*0.7,
viewportFraction: 1.0,
enableInfiniteScroll: true,
reverse: false,
autoPlay: true,
autoPlayInterval: Duration(seconds: 8),
autoPlayAnimationDuration: Duration(milliseconds: 800),
autoPlayCurve: Curves.fastOutSlowIn,
),
items: images.map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
//width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height*0.7,
decoration: BoxDecoration(
color: Colors.amber
),
child: Image.network(i,fit: BoxFit.cover, height: MediaQuery.of(context).size.height*0.7,)
);
},
);
}).toList(),
);
}
class HomePanel extends StatelessWidget {
#override
Widget build(BuildContext context) {
final double height = MediaQuery.of(context).size.height;
List<String> data = <String>["Twitter", "Reddit", "YouTube", "Facebook",
"Vimeo", "GitHub", "GitLab", "BitBucket", "LinkedIn", "Medium",
"Tumblr", "Instagram", "Pinterest"];
List<RaisedButton> myWidgets = data.map((item) {
return new RaisedButton(
child: new Text(item),
onPressed: () async {
}
);
}).toList();
GridView myGrid = GridView.count(
crossAxisCount: 3,
children: myWidgets
);
return Container(
height: height*0.3,
width: MediaQuery.of(context).size.width,
color: Colors.red,
child: myGrid
);
}
}
I'm trying to add a GridView to a Container, but an indent appears at the top. Please tell me how to fix this?
I painted the Container red to show that there is a padding on top. I could not find a solution to this problem on the Internet. I'm new to Flutter, maybe I missed an important point in building this widget.
You can try wrap GridView with a MediaQuery.removePadding() then set removeTop property to True.
MediaQuery.removePadding(
context: context,
removeTop: true,
child: GridView(
.......
)
);
I have used your code pretty much, just for the Carousel, I have used the ListView.builder(). Rest is fine.
The catch is to use Expanded class inside your Column() to take the height automatically for the Carousel
Follow the code along, and see the result as well, no extra space in the UI in the GridView
class _MyHomePageState extends State<MyHomePage> {
List<String> images = [
'https://skalka-app.ru/banners/1.png',
'https://skalka-app.ru/banners/2.png',
'https://skalka-app.ru/banners/3.png',
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: double.infinity,
child: Column(
children: [
// Expanded used to take up the space
Expanded(
// ListView.builder, use your carousel here
child: ListView.builder(
shrinkWrap: true,
itemCount: images.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index){
// look at this as well, no height, only width
// given for the image
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(images[index])
)
)
);
}
)
),
HomePanel()
],
),
)
);
}
}
class HomePanel extends StatelessWidget {
#override
Widget build(BuildContext context) {
final double height = MediaQuery.of(context).size.height;
List<String> data = <String>["Twitter", "Reddit", "YouTube", "Facebook",
"Vimeo", "GitHub", "GitLab", "BitBucket", "LinkedIn", "Medium",
"Tumblr", "Instagram", "Pinterest"];
List<RaisedButton> myWidgets = data.map((item) {
return new RaisedButton(
child: new Text(item),
onPressed: () async {
}
);
}).toList();
GridView myGrid = GridView.count(
crossAxisCount: 3,
children: myWidgets
);
return Container(
height: height*0.3,
width: MediaQuery.of(context).size.width,
color: Colors.red,
child: myGrid
);
}
}
Result
Look at the design closely in the result, no extra spacing or padding