Flutter GridView Item Click Animation - flutter

Grid view item animation
I want individual grid items to animate on click. But on clicking an item, all the items animate simultaneously. How to fix this?
I want individual grid items to animate on click. But on clicking an item, all the items animate simultaneously. How to fix this?
I want individual grid items to animate on click. But on clicking an item, all the items animate simultaneously. How to fix this?
I want individual grid items to animate on click. But on clicking an item, all the items animate simultaneously. How to fix this?
class ChoosePetState extends State with TickerProviderStateMixin
{
late AnimationController _animationController;
late Animation<double> _animation;
#override
void initState()
{
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_animation =
Tween<double>(begin: 1.0, end: 1.2).animate(_animationController);
_animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.reverse();
}
});
}
#override
Widget build(BuildContext context)
{
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(statusBarColor: CustomColor.mailColor));
return SafeArea(
child: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: SizedBox(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.fromLTRB(20.0, 20.0,20.0,30.0),
child: Text(
Strings.choosePet,
style: GoogleFonts.firaSans(
fontSize: 32.0,
fontWeight: FontWeight.bold,
color: CustomColor.colorPrimaryDark.withOpacity(0.7),
),
),
),
),
),
SliverPadding(
padding: const EdgeInsets.fromLTRB(10.0,0.0,10.0,30.0),
sliver: SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.91,
mainAxisSpacing: 1.0,
crossAxisSpacing: 1.0,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: const CircleBorder(),
elevation: 5.0,
shadowColor: CustomColor.mailColor,
child: InkResponse
(
onTap: (){
_animationController.forward();
},
child: ScaleTransition(
scale: _animation,
child: Image.asset(
list[index].url,
fit: BoxFit.cover,
height: 70.0,
width: 70.0,
),
),
),
);
},
childCount: list.length,
),
),
),
],
),
),
);
}
}

you can use my code for answer :
here i used onTapIndex where i can update this value when onclick and that index match then only that widget is animated
class ChoosePetState extends State with TickerProviderStateMixin
{
late AnimationController _animationController;
late Animation<double> _animation;
int onTapIndex = -1;
#override
void initState()
{
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_animation =
Tween<double>(begin: 1.0, end: 1.2).animate(_animationController);
_animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.reverse();
}
});
}
#override
Widget build(BuildContext context)
{
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(statusBarColor: CustomColor.mailColor));
return SafeArea(
child: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: SizedBox(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.fromLTRB(20.0, 20.0,20.0,30.0),
child: Text(
Strings.choosePet,
style: GoogleFonts.firaSans(
fontSize: 32.0,
fontWeight: FontWeight.bold,
color: CustomColor.colorPrimaryDark.withOpacity(0.7),
),
),
),
),
),
SliverPadding(
padding: const EdgeInsets.fromLTRB(10.0,0.0,10.0,30.0),
sliver: SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.91,
mainAxisSpacing: 1.0,
crossAxisSpacing: 1.0,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: const CircleBorder(),
elevation: 5.0,
shadowColor: CustomColor.mailColor,
child: InkResponse
(
onTap: (){
setState(() {
onTapIndex = index;
});
_animationController.forward();
},
child: onTapIndex == index ? ScaleTransition(
scale: _animation,
child: Image.network(
list[index],
fit: BoxFit.cover,
height: 70.0,
width: 70.0,
),
) : Image.network(
list[index],
fit: BoxFit.cover,
height: 70.0,
width: 70.0,
),
),
);
},
childCount: list.length,
),
),
),
],
),
),
);
}
}

Related

Can I add a CarouselSlider to a PageView.builder, no auto scroll

I have a Futurebuilder that works with a ListView.builder that displays on the screen but I wanted to add the carousel_slider package but nothing happens, the Carouselslider is set to auto scroll . I can still so it manually like a list view.
My Code:
There are just 2 elements from the Future on the screen, image and a title image.
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import '../../future_carousel.dart';
class CarouselInformation extends StatelessWidget {
CarouselInformation({Key? key}) : super(key: key);
var datasource = const FutureInformation();
getSliderDetailsEvents() async {
List futureEvents = await datasource.getSliderDetails();
return futureEvents;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sliders')),
body: SingleChildScrollView(
child: Column(
children: [
FutureBuilder(
future: getSliderDetailsEvents(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const CircularProgressIndicator();
} else {
return Column(
children: [
const Text('Block 1'),
CarouselSlider(
options: CarouselOptions(
aspectRatio: 1.5,
viewportFraction: 1.0,
enlargeCenterPage: true,
enlargeStrategy: CenterPageEnlargeStrategy.height,
enableInfiniteScroll: false,
initialPage: 2,
autoPlay: true,
),
items: [
SizedBox(
height: 260,
width: 375,
child: Expanded(
child: PageView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int index) {
return Container(
margin: const EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(5.0)),
child: Stack(
children: [
Image.network(
snapshot.data[index].image,
fit: BoxFit.cover),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Container(
decoration:
const BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(
200, 0, 0, 0),
Color.fromARGB(
0, 0, 0, 0)
],
begin: Alignment
.bottomCenter,
end: Alignment.topCenter,
),
),
padding: const EdgeInsets
.symmetric(
vertical: 10.0,
horizontal: 20.0),
child: Text(
snapshot.data[index].title,
style: const TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight:
FontWeight.bold,
),
),
),
),
],
),
),
);
},
),
),
),
]),
],
);
}
}),
],
),
),
);
}
}
Result of the code above:
I want to achieve the same effect as the bottom Widget as on this clip but the images on that clip which is working are hardcoded not from a Future.
https://www.screencast.com/t/ouWL8AkSuI

How i can set custom animation between slide in CarouselSlider flutter

I have created an image slider using CarouselSlider, I want to set animation when the page change but when the page change it will not reflect to the dot indicator
slider and dot position
here is the full code of my slider
final RxInt _current = 0.obs;
PageController pageViewController = PageController();
final CarouselController _controller = CarouselController();
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) {
for (var imageUrl in images) {
precacheImage(NetworkImage(imageUrl), context);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Slider demo')),
body: Obx(
() => Stack(
children: [
Positioned(
bottom: 40.0,
left: 0.0,
right: 0.0,
child: Padding(
padding: const EdgeInsets.only(top: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedSmoothIndicator(
activeIndex: _current.value,
count: images.length,
effect: ExpandingDotsEffect(
radius: 10,
dotWidth: 10,
dotHeight: 10,
activeDotColor: Colors.green,
expansionFactor: 4,
dotColor: Colors.green.withOpacity(0.17),
), // your preferred effect
onDotClicked: (index) {
pageViewController.animateToPage(
index,
duration: const Duration(milliseconds: 500),
curve: Curves.ease,
);
},
)
],
),
),
),
Padding(
padding: const EdgeInsets.only(top: 50),
child: Expanded(
child: Container(
child: CarouselSlider.builder(
itemCount: images.length,
carouselController: _controller,
options: CarouselOptions(
autoPlay: true,
aspectRatio: 14 / 8.5,
viewportFraction: 0.8,
enlargeCenterPage: true,
autoPlayAnimationDuration: const Duration(seconds: 2),
autoPlayInterval: const Duration(seconds: 4),
autoPlayCurve: Curves.easeInOutSine,
onPageChanged: (index, reason) {
_current.value = index;
},
scrollPhysics: const BouncingScrollPhysics(),
),
itemBuilder: (context, index, realIdx) {
return index == _current.value
? ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
child: Stack(
children: [
Image.network(
images[index],
fit: BoxFit.cover,
width: 1000,
height: 170,
)
],
),
)
: Padding(
padding: const EdgeInsets.only(top: 25),
child: Container(
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
child: Stack(
children: [
Image.network(
images[index],
fit: BoxFit.cover,
width: 1000,
height: 172,
)
],
),
),
),
);
},
)),
),
),
],
),
),
);
}

Flutter hero animation not working, same tag but still not animating in new screen

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,
),
),
),
),
),
);
}
}

Is there a way to smoothly tween the position of list items when adding or removing from an AnimatedList?

I have an AnimatedList, and I can add and remove items just fine. My issue is that when I add an item to the top of the list, the items below it jump down to make space for it, instead of transitioning smoothly.
I found this answer which provides a solution to smoothly animating items up when one is removed, but I can't get the same solution to work for inserting items.
This is what I have so far. As the item is added to the top of the list, the elements below it shift smoothly. However, the scale-in transition of the new element also follows this shift.
class Foo extends StatefulWidget {
Foo({Key key}) : super(key: key);
final kDuration = const Duration(seconds: 1);
#override
_FooState createState() => _FooState();
}
class _FooState extends State<Foo> {
final _key = GlobalKey<AnimatedListState>();
List<Color> _items;
int _counter;
#override
void initState() {
super.initState();
_items = <Color>[];
_counter = 0;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Animated List Spike'),
),
body: AnimatedList(
key: _key,
initialItemCount: _items.length,
itemBuilder: (context, index, animation) {
return Stack(
children: [
// This transition creates the smooth shift-down effect.
SizeTransition(
sizeFactor: animation.drive(Tween(begin: 0, end: 1)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 64,
color: Colors.transparent,
),
),
),
// This is the content we actually want to show.
Align(
alignment: Alignment.topCenter,
heightFactor: 0,
child: SlideTransition(
position: Tween(
begin: Offset(0, -0.5),
end: Offset(0, 0),
).animate(animation),
child: ScaleTransition(
scale: CurveTween(curve: Curves.easeOutBack)
.animate(animation),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
color: _items[index],
child: Container(height: 64),
),
),
),
),
),
],
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: addItem,
child: const Icon(Icons.add),
),
);
}
void addItem() {
_items.insert(0, Colors.primaries[_counter++ % Colors.primaries.length]);
_key.currentState.insertItem(0, duration: widget.kDuration);
}
}
Is there a way to have the elements shift smoothly and then have the new element scale in?
The solution was to use Interval curves. By having one curve end at the 0.5 mark and the other begin there, I was able to make the elements slide down, and then have my new element scale in.
CurveTween(
curve: Interval(
0.5, // Start halfway through the animation.
1.0, // Stop at the end of the animation.
curve: Curves.easeOutBack,
),
).animate(animation),
Full solution for question:
itemBuilder: (context, index, animation) {
return Stack(
children: [
SizeTransition(
sizeFactor: animation.drive(
CurveTween(
curve: Interval(
0.0,
0.5,
curve: Curves.linear,
),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 64,
color: Colors.transparent,
),
),
),
Align(
alignment: Alignment.topCenter,
heightFactor: 0,
child: ScaleTransition(
scale: CurveTween(
curve: Interval(
0.5,
1.0,
curve: Curves.easeOutBack,
),
).animate(animation),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
color: _items[index],
child: Container(height: 64),
),
),
),
),
],
);

How to create multiple horizontal `GridView` in the same screen using flutter

Is there's a way to create a lists of GridViews as the below image in one screen...
I have a some Screen as the below one:
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
var _showOnlyFavorites = false;
AnimationController animationController;
bool multiple = true;
#override
void initState() {
animationController = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
super.initState();
}
Future<bool> getData() async {
await Future<dynamic>.delayed(const Duration(milliseconds: 0));
return true;
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.white,
body: FutureBuilder<bool>(
future: getData(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
} else {
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
appBar(),
Expanded(
child: FutureBuilder<bool>(
future: getData(),
builder:
(BuildContext context, AsyncSnapshot<bool> snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
} else {
return PropertiesGrid(_showOnlyFavorites);
}
},
),
),
],
),
);
}
},
),
);
}
Widget appBar() {
return SizedBox(
height: AppBar().preferredSize.height,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8, left: 8),
child: Container(
width: AppBar().preferredSize.height - 8,
height: AppBar().preferredSize.height - 8,
),
),
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 4),
child:
Image.asset('assets/images/logo.png', fit: BoxFit.contain),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 8, right: 8),
child: Container(
width: AppBar().preferredSize.height - 8,
height: AppBar().preferredSize.height - 8,
color: Colors.white,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius:
BorderRadius.circular(AppBar().preferredSize.height),
child: Icon(
multiple ? Icons.dashboard : Icons.view_agenda,
color: AppTheme.dark_grey,
),
onTap: () {
setState(() {
multiple = !multiple;
});
},
),
),
),
),
],
),
);
}
}
as I have a widget which have the GridView.builder as the below code:
import 'package:aradi_online_vtwo/providers/properties.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/properties.dart';
import './property_item.dart';
class PropertiesGrid extends StatelessWidget {
final bool showFavs;
PropertiesGrid(this.showFavs);
#override
Widget build(BuildContext context) {
final productsData = Provider.of<Properties>(context);
final products = showFavs ? productsData.favoriteItems : productsData.items;
return GridView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: products.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
// builder: (c) => products[i],
value: products[i],
child: PropertyItem(
// products[i].id,
// products[i].title,
// products[i].imageUrl,
),
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: 3 / 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
), scrollDirection: Axis.horizontal,
);
}
}
I tried to set the height of the grid by wrapping it with a Container and set the height of it as to add more grids but it doesn't work.
and here's my Grid Item widget code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/property.dart';
class PropertyItem extends StatelessWidget {
#override
Widget build(BuildContext context) {
final property = Provider.of<Property>(context, listen: false);
return InkWell(
onTap: () => {},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
elevation: 7,
margin: EdgeInsets.all(2),
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
),
// color: Colors.transparent,
child: Image.asset(
property.image,
fit: BoxFit.fill,
),
),
Positioned(
top: 8,
right: 8,
child: Consumer<Property>(
builder: (ctx, property, _) => IconButton(
icon: Icon(
property.isFavorite ? Icons.favorite : Icons.favorite_border,
),
color: Colors.red,
onPressed: () {
property.toggleFavoriteStatus();
},
),
),
),
Positioned(
right: 20,
top: 100,
child: Container(
width: 300,
color: Colors.black54,
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 20,
),
child: Text(
property.title,
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
softWrap: true,
overflow: TextOverflow.fade,
),
),
)
],
),
),
);
}
}
You'll need a column where each ListView or GridView is wrapped inside a SizedBox (if you have a specific height) and you also can use Expanded to take whatever available space (like the last one in the given example):
You can post the code below in dartpad.dev and see how it works:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyApp(),
));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (c, i) {
return Card(
child: Container(
height: 100,
width: 100,
child: Center(child: Text("$i")),
),
);
},
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
child: Text("The Second List"),
alignment: Alignment.centerRight,
),
),
SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (c, i) {
return Card(
child: Container(
height: 100,
width: 100,
child: Center(child: Text("$i")),
),
);
},
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
child: Text("The Third List"),
alignment: Alignment.centerRight,
),
),
Expanded(
//height: 200,
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
scrollDirection: Axis.vertical,
itemCount: 20,
itemBuilder: (c, i) {
return Card(
child: Container(
height: 100,
width: 100,
child: Center(child: Text("$i")),
),
);
},
),
),
],
),
);
}
}