I am facing a problem with displaying image in the grid view in Flutter.
1) I am fetching 25 images from Firestore but these images are not loaded from top to bottom. these images are loaded asynchronously. I want to load all the images one by one from top to bottom so, the other images which are not on the screen should be loaded when they are scrolled.
2) When I tap any of the Image it is taken to FullScreen Page. I have used navigator. push to navigate from one page to another. But, when I again tap back button in FullScreen Page, all the Images get loaded from the beginning. I want all the images not to be loaded again.
This is the code Where Images are loaded.
class CategoryGrid extends StatefulWidget {
#override
_CategoryGridState createState() =>
_CategoryGridState();
}
class _CategoryGridState extends State<CategoryGrid> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: StreamBuilder(
stream: Firestore.instance.collection(label).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
child: Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.black54),
),
),
);
} else {
return SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
height: 50.0,
margin: EdgeInsets.only(top: 45.0),
child: Text(
displayText,
style: GoogleFonts.poppins(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 25.0,
),
),
),
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.6,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
padding: EdgeInsets.all(10.0),
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot imgUrl =
snapshot.data.documents[index];
if (imgUrl == null) {
return CircularProgressIndicator();
} else {
return GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return FullScreenPage(
image: "${imgUrl['image']}");
}));
},
child: Container(
child: ClipRRect(
borderRadius:
BorderRadius.all(Radius.circular(5.0)),
child: FadeInImage(
image: NetworkImage(
"${imgUrl['image']}",
),
fit: BoxFit.fill,
placeholder: AssetImage(
'images/Loadinghorizontal.jpg'),
),
),
),
);
}
},
),
],
),
),
);
}
},
),
);
}
}
This is the code which is pushed to another screen while tapped
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black54,
body: SafeArea(
child: Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image.network(
image,
fit: BoxFit.cover,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(),
);
},
),
),
Positioned(
top: 12.0,
left: 12.0,
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
),
),
),
],
),
),
);
Use the cached_network_image package, it is perfect because it allows you to put the images you have downloaded in the cache memory of the phone, so here is how you are going to do it :
- You will update your pubspec.yaml file
By adding this in the dependencies cached_network_image: ^2.2.0+1
You do flutter pub get and voila
- Small Example
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/200x150",
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
colorFilter:
ColorFilter.mode(Colors.red, BlendMode.colorBurn)),
),
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
Related
I have a stateful widget called AddPhotosView that contains all the logic i am trying to achieve, i am using this package photo_manager to display images in the user's devices in a GridView.builder and i am trying to use image_cropper to be able to crop the image the user selects in the from the grid view.
This is the full code of my stateful widget :
class AddPhotosView extends StatefulWidget {
#override
State<AddPhotosView> createState() => _AddPhotosView();
}
class _AddPhotosView extends State<AddPhotosView> {
List<Widget> _mediaList = [];
int currentPage = 0;
int lastPage;
#override
void initState() {
super.initState();
_fetchNewMedia();
}
_handleScrollEvent(ScrollNotification scroll) {
if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
if (currentPage != lastPage) {
_fetchNewMedia();
}
}
}
_fetchNewMedia() async {
var result = await PhotoManager.requestPermissionExtend();
if (result != null) {
// success
//load the album list
List<AssetPathEntity> albums =
await PhotoManager.getAssetPathList(onlyAll: true);
print(albums);
List<AssetEntity> media =
await albums[0].getAssetListPaged(page: currentPage, size: 60);
print(media);
List<Widget> temp = [];
for (var asset in media) {
temp.add(
FutureBuilder(
future: asset.thumbnailDataWithSize(
ThumbnailSize(200, 200),
),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return GestureDetector(
onTap: () async {
File file = await asset.file;
print(file.toString());
},
child: Stack(
children: <Widget>[
Positioned.fill(
child: Image.memory(
snapshot.data,
fit: BoxFit.cover,
),
),
if (asset.type == AssetType.video)
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 5, bottom: 5),
child: Icon(
Icons.videocam,
color: Colors.white,
),
),
),
],
),
);
return Container();
},
),
);
}
setState(() {
_mediaList.addAll(temp);
currentPage++;
});
} else {
// fail
PhotoManager.openSetting();
}
}
#override
Widget build(BuildContext context) {
// final controller = Get.put(EServicesController());
return Scaffold(
appBar: AppBar(
toolbarHeight: 50,
backgroundColor: Colors.white,
title: Column(
children: [
Text(
"Add Photos".tr,
style: GoogleFonts.poppins(
color: Color(0xff000000),
fontSize: 16,
fontWeight: FontWeight.w600),
),
],
),
centerTitle: false,
elevation: 0.5,
automaticallyImplyLeading: false,
leadingWidth: 15,
leading: new IconButton(
icon: new Icon(Icons.arrow_back_ios, color: Color(0xff3498DB)),
onPressed: () => {Get.back()},
),
),
body: Column(
children: [
Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.only(
left: 45.0, right: 45, top: 22, bottom: 35),
child: Container(
height: 280,
width: 280,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: Stack(
fit: StackFit.expand,
children: [
Image.asset(
'assets/icon/image-test.png',
fit: BoxFit.cover,
),
ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.white.withOpacity(0.3),
BlendMode.srcOut), // This one will create the magic
child: Stack(
fit: StackFit.expand,
children: [
Container(
decoration: BoxDecoration(
color: Colors.black,
backgroundBlendMode: BlendMode
.dstOut), // This one will handle background + difference out
),
Align(
alignment: Alignment.topCenter,
child: Container(
margin: const EdgeInsets.only(top: 80),
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(100),
),
),
),
],
),
),
],
),
),
),
),
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scroll) {
_handleScrollEvent(scroll);
return;
},
child: Padding(
padding: const EdgeInsets.all(22.0),
child: GridView.builder(
itemCount: _mediaList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return _mediaList[index];
}),
),
),
),
],
),
//bottomNavigationBar: SendUpdateButton(),
);
}
}
My photo_manager package is working and displaying my images correctly, but i want to more with it.
Want i am trying to achieve :
Get the image path of an image selected by the user with the photo_manager package.
I also want to be able select multiple images at once or a single image when tapped from the grid view.
Then render the image selected by the user in the Container carrying the image.asset() widget.
Crop the image using the flutter package image_cropper.
Getting the file path of an image selected by the user is very important as i believe it can help me achieve many other logic.
I have tried getting the image path by wrapping the container carrying the image.asset with
GestureDetector(
onTap: () async {
File file = await asset.file;
print(file.toString());
},
But it don't to work :(
Please i would be entirely grateful if someone help me out with what i am trying to achieve as i have been struggling to achieve this for sometime now in my project.
NB: I am using the package: photo_manager because it helps me to customise the UI of where the user's gallery can be displayed, other packages has an out-of-the-box UI which i don't want. So i will be open to using another package for only cropping the image if possible.
I have this ListView and I want to add a MaterialPageRoute. How can I do it that it opens a diverent page when I click on anoter item from the ListView.
(I dont want to want to send selected item data to next screen)
Example:
I click on ExamplePage it shod Navigate to ExamplePage() when I click on TestPage it shod Navigate to TestPage()
Expanded(
child: ListView.builder(
itemCount: categories.length,
itemBuilder: (BuildContext ctx, int index) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return ExamplePage();
},
),
);
},
child: Container(
margin: const EdgeInsets.all(20),
height: 150,
child: Stack(
children: [
Positioned.fill(
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset(
'images/${categories[index].imgName}.png',
fit: BoxFit.cover),
),
),
Positioned(
bottom: 0,
child: Padding(
padding: const EdgeInsets.all(10.0),
Text(
categories[index].name,
style: const TextStyle(fontSize: 25),
)
),
)
],
),
),
);
},
),
)
I want to add the code below to my existing card constructor but I am unable to do so
child: Container(
height: 50,
width: 100,
child: FutureBuilder<Uri>(
future: createDynamicLink(), //return link
builder: (context, snapshot) {
if (snapshot.hasData) {
Uri? uri = snapshot.data;
return FlatButton(
color: Colors.amber,
onPressed: () => FlutterShare.share(title: 'Example share',
text: 'Example share text',
linkUrl: uri.toString(),
chooserTitle: 'Example Chooser Title'),
child: Text('Share'),
);
}
}),
),
And this is the card widget with which I want to add the future function
child: Container(
margin: const EdgeInsets.only(top: 120.0),
child: GridView(
physics: BouncingScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
children: events.map((title){
return Scaffold(body:
Container(decoration: BoxDecoration(
image:DecorationImage(
image: AssetImage("assets/bg.png"), fit: BoxFit.cover),),
return GestureDetector(
child: Card(
margin: const EdgeInsets.all(14.0),
),
onDoubleTap: () {
Fluttertoast.showToast(msg: "msg");
},
);
}).toList(),
),
I want to be able to use the future function along with the flutterShare functionality when I click on one of the grid card
The event list used above:
List<String> events = [
"Emergency SMS",
"Call",
"Navigation"
];
Truly appreciate any help!
I have memory image in my downloads screen as grid tile and when clicked, it will animated to next full image view page where whole body is imageview so i want to animated to new screen with hero animation but it is not working.
I even provided the same tag but still not animating.
The problem is that it work with Navigator.push method but here i am using Get.toNamed() method as i am passing arguments to next screen controller class , but with this navigation it is not animating. how to do hero animation with Getx navigation?
dowloads.dart
class Downloads extends StatelessWidget {
final photos = DatabaseProvider.db.getPictures();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: brandName('Down', 'loads'),
elevation: 0.0,
),
body: FutureBuilder<List<Picture>>(
future: photos,
builder: (BuildContext context, AsyncSnapshot<List<Picture>> snapshot) {
if (snapshot.hasData) {
if (snapshot.data.length == 0) {
return Center(
child: Text(
'No data available for downloads',
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.w300, fontSize: 18),
));
} else {
return AnimationLimiter(
child: GridView.count(
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 24),
physics: BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
crossAxisCount: 2,
childAspectRatio: 0.6,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
children: snapshot.data.map((wallpaper) {
return AnimationConfiguration.staggeredGrid(
position: 2,
duration: Duration(milliseconds: 500),
columnCount: 3,
child: ScaleAnimation(
duration: Duration(milliseconds: 900),
curve: Curves.fastLinearToSlowEaseIn,
scale: 1.5,
child: FadeInAnimation(
child: GridTile(
child: GestureDetector(
onTap: () {
Get.toNamed(
'/image_view',
arguments: [
wallpapers[index].src.portrait,
wallpapers[index].photographer,
wallpapers[index].photographerUrl
],
);
},
child: Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Hero(
tag: 'picture:${wallpaper.imageUrl}',
child: Image.memory(
wallpaper.picture,
fit: BoxFit.cover,
),
),
),
),
),
),
),
),
);
}).toList(),
),
);
}
} else {
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}
imageVeiw.dart
class LocalImageView extends StatelessWidget {
final Uint8List pictureBytes;
final String imageUrl;
final String title;
LocalImageView({this.pictureBytes, this.imageUrl, this.title});
#override
Widget build(BuildContext context) {
print(imageUrl);
return Container(
child: LayoutBuilder(
builder: (context, constraints) => SingleChildScrollView(
reverse: true,
scrollDirection: Axis.horizontal,
child: SizedBox(
height: constraints.biggest.height,
child: Hero(
tag: 'picture:$imageUrl',
child: Image.memory(
pictureBytes,
fit: BoxFit.cover,
),
),
),
),
),
);
}
}
I m using firestore for data and firestorage for files and images, so i m using StreamBuilder to load list as per firestore data and i want to download images as per firestore file name, but its not happening
I tried is, created a new method there I did code to get download url , but it is not working
child: StreamBuilder(
stream: Firestore.instance.collection('data').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return Container(
child: Center(child: CircularProgressIndicator()));
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Column(
children: <Widget>[
Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Image.network(
imageLoader(snapshot.data.documents[index].data['flie_ref'].toString()),
fit: BoxFit.fill,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
elevation: 0,
margin: EdgeInsets.all(10),
),
Padding(
padding: EdgeInsets.only(left: 6, right: 6),
child: Card(
elevation: 0,
child: ExpansionTile(
leading: CircleAvatar(
child: Text(snapshot
.data.documents[index].data['amount']
.toString()),
),
title: Text(
snapshot
.data.documents[index].data['desc']
.toString(),
overflow: TextOverflow.ellipsis,
),
children: <Widget>[
Text(snapshot
.data.documents[index].data['desc']
.toString()),
SizedBox(
height: 10,
),
],
),
),
),
],
),
);
});
},
),
Future imageLoader(String string) async {
var url =
await FirebaseStorage.instance.ref().child(string).getDownloadURL();
setState(() {
return url;
});
}
I expect output load respective image but the output is Error: The argument type 'Future' can't be assigned to the parameter type 'Widget'