Example image with dot indicator
I have multiple images each with their own redirect link. Currently this works fine at displaying using a list view build to display the images inside a gesture detector.
However, I’d like to add a dot indicator to show which image is being viewed. How can I get the index of the image being displayed? Or increase / decrease a counter when swiping left or right.
You should post the code of what you have done so people can see what your exact problem is.
If you're using ListView.builder, you can get index from itemBuilder. Then create a variable to hold the value of that index when you interact with the list
int currentIndex;
itemCount: list.length,
itemBuilder: (context, index) {
currentIndex = index;
}
Then below the listView, add a custom dot indicator list.
Row(
children: [
for(int i = 0; i < list.length; i++)
Container(
height: 10, width: 10,
decoration: BoxDecoration(
color: i == currentIndex ? Colors.white : Colors.grey,
borderRadius: BorderRadius.circular(5)
)
)
]
)
You can get the index from itemBuilder. and change index by pass index in activeIndex parameter of AnimatedSmoothIndicator.
for smooth indicator smooth_page_indicator
AnimatedSmoothIndicator(
activeIndex: index,
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,
);
},
)
For handling pageview by clicking on dot
pageViewController.animateToPage(
index,
duration: const Duration(milliseconds: 500),
curve: Curves.ease,
);
Related
I am trying to implement a design that looks like this:
In the example the currentItem in the middle is of size 19 and its opacity is at 100%. The 2nd item below/above it is of size 13 with an opacity of 50% and the third items are of size 9 and with anopacity of 25%.
How could I implement something like this with Flutter? I've tried multiple things, for example ListWheelScrollView but I couldn't get the desired design.
One more requirement: The currentItem should be tappable.
Any idea how I could get this done?
A solution could be to use a CupertinoPicker to achieve the design you're looking for.
For example, using a StatefulWidget to update and retrieve the currentIndex :
static const double _containerHeight = 500;
int currentIndex = 0;
#override
Widget build(BuildContext context) {
return SizedBox(
height: _containerHeight,
child: CupertinoPicker.builder(
itemExtent: _containerHeight / 3,
diameterRatio: 20,
useMagnifier: false,
selectionOverlay: null,
onSelectedItemChanged: (value) => setState(() => currentIndex = value),
itemBuilder: (context, index) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: TextStyle(fontSize: getFontSizeFromIndex(index)),
child: Text(
'Das ist ein Text',
style: TextStyle(
color:
Colors.white.withOpacity(getOpacityFromIndex(index))),
),
),
],
);
},
),
);
}
double getFontSizeFromIndex(int index) => index == currentIndex
? 19
: index == currentIndex - 1 || index == currentIndex + 1
? 13
: 9;
double getOpacityFromIndex(int index) => index == currentIndex
? 1
: index == currentIndex - 1 || index == currentIndex + 1
? .5
: .25;
To go further, you should considerate :
debouncing the onSelectedItemChanged in order to prevent abusive rebuilds ;
using a custom ScrollController and do some linear interpolations in order to make the animation smoother.
Here is the result :
I have a list of report items.
I initialised a scrollController here:
class ReportList extends StatelessWidget {
ReportList({Key? key}) : super(key: key);
final ScrollController scrollController = ScrollController();
scrollDown() {
scrollController.animateTo(scrollController.position.maxScrollExtent,
duration: const Duration(seconds: 1), curve: Curves.bounceOut);
}
I also have a list view with the controller, and I want the list view to stroll to bottom when the last item is selected:
ListView.separated(
controller: scrollController,
itemCount: 13,
itemBuilder: (_, index) {
return Column(
children: [
ReportItem(
onClick: () {
viewModel.setCommentReportStat(index);
if (index == 12 && viewModel.commentReportStat[12]) {
scrollDown();
}
debugPrint(viewModel.commentReportStat.toString());
},
isSelected: viewModel.commentReportStat[index],
title: CommentReport.items[index],
),
//show additional message field to the user
if (index == 12 && viewModel.commentReportStat[12])
AnimatedContainer(
duration: const Duration(seconds: 1),
child: MessageField(commentId: commentId),
)
],
);
},
separatorBuilder: (_, index) => const SizedBox(
height: 16,
),
)
Everything about the state management works since the message field appears as the user clicks on the last item.
My problem in general is that neither does message field appear with the animated container which is wrapped around it, nor does it scroll down!
How it is right now:
What I want to happen:
(If I scroll manually, I can see the entire message field, but I want this to be automated using the scroll controller)
instead of this
if (index == 12 && viewModel.commentReportStat[12])
AnimatedContainer(
duration: const Duration(seconds: 1),
child: MessageField(commentId: commentId),
)
use this
AnimatedCrossFade(
duration: const Duration(seconds: 1),
firstChild: const SizedBox(),
secondChild: MessageField(commentId: commentId),
crossFadeState: (index == 12 && viewModel.commentReportStat[12]) ? CrossFadeState.showSecond : CrossFadeState.showFirst,
)
You can also use this function if you want to scroll to the end of the page when an event occurs.
toMaxScroll() {
_scrollController.animateTo(_scrollController.position.maxScrollExtent,duration:const Duration(milliseconds:300),curve:Curves.easeIn);
}
Helllo
I have a Product Category screen on my Flutter App. I have added a IconButton for animation for 'Add To Cart' action. But if you click add to cart icon on product image, all icons are animating. I want to animate only one icon. Can you help me?
IconButton(
icon: AnimatedSwitcher(
duration: const Duration(milliseconds: 350),
transitionBuilder: (child, anim) => RotationTransition(
turns: child.key == ValueKey('icon1')
? Tween<double>(begin: 1, end: 0.75).animate(anim)
: Tween<double>(begin: 0.75, end: 1).animate(anim),
child: ScaleTransition(scale: anim, child: child),
),
child: _currIndex == 0
? Icon(Icons.close, key: const ValueKey('icon1'))
: Icon(
Icons.arrow_back,
key: const ValueKey('icon2'),
)),
onPressed: () {
setState(() {
_currIndex = _currIndex == 0 ? 1 : 0;
});
},
),
I think the issue is because you are using _currIndex in each IconButton, so when you change one you make setState to change the value then every product gets affected by this change.
what you have to do is separate each product from the other...
one way is to make _currIndex a local variable if you use ListView put the variable in its scope but I doubt this works
the other way is to make a list of Booleans of size equal to the number of products and you can check if the _currIndex of a product is 0 or 1 by accessing the List with the index of the ListView.
so it will look like this:
// at the beginning of the file you will have a list of booleans with all values set to zero by doing this
List<bool> isInCart = List.filled(numberOfProducts, false);
// then you will do your code but with the list not the _currIndex
IconButton(
icon: AnimatedSwitcher(
duration: const Duration(milliseconds: 350),
transitionBuilder: (child, anim) => RotationTransition(
turns: child.key == ValueKey('icon1')
? Tween<double>(begin: 1, end: 0.75).animate(anim)
: Tween<double>(begin: 0.75, end: 1).animate(anim),
child: ScaleTransition(scale: anim, child: child),
),
// if the element is true then this product is in the cart otherwise its not
child: isInCart[index]
? Icon(Icons.close, key: const ValueKey('icon1'))
: Icon(
Icons.arrow_back,
key: const ValueKey('icon2'),
)),
onPressed: () {
setState(() {
// you just invert the value here
isInCart[index] = !isInCart[index];
});
},
),
I am using smooth_page_indicator and trying to add some styling to my smooth page indicator, I would like to add borders to it and make it look like this;
But I ended up having something that looks like this;
I am still new to programming in dart and flutter and need help here. I discovered the PaintingStyle.stroke property seems to get it, but not exactly. I searched around but haven't seen any in-depth tutorials on Smooth Page Indicators that can help.
Here is my code;
child: SmoothPageIndicator(
controller: controller,
count: 3,
effect: ScrollingDotsEffect(
paintStyle: PaintingStyle.stroke,
dotColor: Color(0xFFACACAC),
activeDotColor: Color(0xFFACACAC),
),
onDotClicked: (index) => controller.animateToPage(
index,
duration: Duration(milliseconds: 500),
curve: Curves.easeIn,
),
),
How can I target the color inside the borders? I felt the activeDotColor would do that.
I wanted to achieve the same and did it like this:
SmoothPageIndicator(
controller: _page_controller,
count: 3,
effect: SlideEffect(
paintStyle: PaintingStyle.stroke,
activeDotColor: Colours.white,
dotColor: Colours.white,
dotHeight: 13.h,
dotWidth: 13.w),
onDotClicked: (index) {
_page_controller.animateToPage(index,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut);
},
)
I would recommend to try and check if the color is being fetched correctly (check the hex)
Bubble effect animation on tap:
How can I make this bubble effect animation using the Flutter app?
You can create the bubble effect similar to this using pimp_my_button package. This package is not on pub.dev so you need to install it via github.
You can install it as following
pimp_my_button:
git:
url: git://github.com/Norbert515/pimp_my_button.git
After that you need to implement the button as following
PimpedButton(
particle: DemoParticle(),
pimpedWidgetBuilder: (context, controller) {
return FloatingActionButton(onPressed: () {
controller.forward(from: 0.0);
},);
},
),
You can test it and you can see you have this fireworks effect on you button.
Now to create a custom animation you have to create your own particle animation.
I am attaching a simple bubble animation below you can tweak it to your need.
class MyParticle extends Particle {
#override
void paint(Canvas canvas, Size size, progress, seed) {
int randomMirrorOffset = 6;
CompositeParticle(children: [
// Firework(),
CircleMirror(
numberOfParticles: 16,
child: AnimatedPositionedParticle(
begin: Offset(0.0, 20.0),
end: Offset(0.0, 60.0),
child: FadingCircle(radius: 3.0, color: Colors.pink),
),
initialRotation: -pi / randomMirrorOffset),
CircleMirror.builder(
numberOfParticles: 16,
particleBuilder: (index) {
return IntervalParticle(
child: AnimatedPositionedParticle(
begin: Offset(0.0, 30.0),
end: Offset(0.0, 50.0),
child: FadingCircle(radius: 3.0, color: Colors.pink),
),
interval: Interval(
0.5,
1,
));
},
initialRotation: -pi / randomMirrorOffset ),
]).paint(canvas, size, progress, seed);
}
}
Now replace the DemoParticle() with MyParticle() & you will have a bubble ripple effect.
To keep repeating the bubble animation, do following.
Change
controller.forward(from: 0.0);
to
controller.repeat(period: Duration(seconds: 1));
Here you can change or skip the animation duration using period property.