onSelectedItemChanged is working perfectly in CupertinoPicker/CupertinoDatePicker
But I also want to pick value when user clicked the value.
Currently, user must scroll in order to pick the value, and as far as I know CupertinoPicker/CupertinoDatePicker doesn't have onTap, onPressed functions
How can I solve this issue
Unfortunately, the gesture detection inside CupertinoPicker/CupertinoDatePicker is not supported for now. When you trace the code inside CupertinoPicker, it leads to use ListWheelScrollView at the end and it does not respond to the tap event.
Discussion thread on GitHub:
Tapping items is not working in CupertinoPicker
ListWheelScrollView children do not respond on gesture (onTap) events
There is a workaround solution by using package clickable_list_wheel_view (fixed height for the child widget, mentioned here)
I found a workaround by using flutter_portal.
final ScrollController scrollController = FixedExtentScrollController();
#override
Widget build(BuildContext context) {
return Container(
height: 100,
child: Portal(
child: CupertinoPicker.builder(
scrollController: scrollController,
childCount: itemCount,
itemBuilder: (context, index) {
return PortalTarget(
anchor: Aligned(
follower: Alignment.center,
target: Alignment.center,
),
portalFollower: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
scrollController.animateToItem(index,
duration: Duration(milliseconds: 250),
curve: Curves.decelerate);
},
),
child: Text(index.toString()),
);
},
),
)
);
}
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => // write what you want to do,
child: CupertinoPicker(),
),
Related
Does anyone have an opinion about handling this animation in flutter?
when u swipe left the front card goes left and left bottom then the behind card size becomes bigger and replace the first card.
I did something similar once. What you want to do is use a GestureDetector and listen to onPanUpdate, add a child with a AnimatedPositioned and if needed a Rotated widget.
You can take a look at this gist which is a class from a hackathon we did.
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) => GestureDetector(
onPanStart: _onPanStart,
onPanEnd: _onPanEnd,
onPanUpdate: _onPanUpdate,
child: Stack(
clipBehavior: Clip.none,
children: [
AnimatedPositioned(
duration: Duration(milliseconds: _duration),
top: _positionY,
left: _positionX,
child: Container(constraints: BoxConstraints(maxHeight: constraints.maxHeight, maxWidth: constraints.maxWidth), child: widget.child))
],
),
),
);
}
I would combine those two:
1 - AnimatedPositioned - to move the widget by some offset
2 - AnimatedRotation - so that you can turn the rotation while it is moving
I am using a ListWheelScrollView more specific a clickable one.
(clickable_list_wheel_view)
I am trying to have a button inside my itemWidget but I can not click it. This is my list:
return ClickableListWheelScrollView(
scrollController: _scrollController,
itemHeight: _itemHeight,
itemCount: months.length,
scrollOnTap: true,
onItemTapCallback: (index) {
print(index)
},
child: ListWheelScrollView.useDelegate(
controller: _scrollController,
itemExtent: _itemHeight,
physics: FixedExtentScrollPhysics(),
diameterRatio: 3,
squeeze: 0.95,
onSelectedItemChanged: (index) {
// print(index);
},
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) => MonthCardWidget(
month: months[index],
),
childCount: months.length,
),
),
);
Inside my MonthCardWidget I have this simple button:
Container(
height: 45,
width: 45,
color: Colors.transparent,
child: IconButton(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
onPressed: () {
print('flip');
},
icon: SvgPicture.asset('assets/images/flip.svg',
height: 20, width: 20),
),
),
Everything is getting displayed just fine but onPressed is never called on my button. I guess the GestureDetector is overriding the button? Is there any workaround for this? I couldn't find anything on this.
The GestureDetector is simply on top of the listView:
#override
Widget build(BuildContext context) {
if (_listHeight == .0) {
return MeasureSize(
child: Container(
child: widget.child,
),
onChange: (value) {
setState(() {
_listHeight = value.height;
});
},
);
}
return GestureDetector(
onTap: _onTap,
onTapUp: (tapUpDetails) {
_tapUpDetails = tapUpDetails?.localPosition;
},
child: widget.child,
);
}
The click will never reach your GestureDetector, you need to specify the click in the ClickableListWheelScrollView here:
onItemTapCallback: (index) {
print(index)
},
If the print you have there isn't printing, then get with the ClickableListWheelScrollView plugin publisher. You'll need logic to reach the changes you want with the button based on the index number passed from the ClickableListWheelScrollView I think....so a map of setState functions might do it.
Think of the button just being a view, and you're just running the function logic in parallel to each button based on its position in the list. The button is buried under the list, you need a layer above the ListWheel, which is the Clickable plugin, but obviously it becomes disconnected from the button, so you need to create logic to link them based on the index value, which is the position of the object in the list.
What I typically do:
onSelectedItemChanged: (index) {
// print(index);
},
This Will return the index position of the item in your list. Use this to change a variable.
Wrap your scroller in a GestureDetector.
In the Gesture Detector, write or call a function that takes your index position variable and performs your function as you desire.
If you need to control where in your list position the scroll begins, or remember its position when you rebuild etc, use a FixedExtentScrollController with an initialItem.
Everything slaves off the index of the onSelectedItemChanged in this way, and you use the FixedExtentScrollController to set a starting position at build. Remember that the list begins at 0.
In flutter there is a pack for flipping card, But when tap its first view - turns into second view. Same as, its turn into second to first view.
But I want to turn into first to second only. Not again reversible.How can I make it, pls.
You can also configure the card to only flip when desired by using a GlobalKey to toggle the cards:
GlobalKey<FlipCardState> cardKey = GlobalKey<FlipCardState>();
#override
Widget build(BuildContext context) {
return FlipCard(
key: cardKey,
flipOnTouch: false,
front: Container(
child: RaisedButton(
onPressed: () => cardKey.currentState.toggleCard(),
child: Text('Toggle'),
),
),
back: Container(
child: Text('Back'),
),
);
}
You can do some logic to togglecard once and then the change the onPressed to an empty function
I need help to do the following: when I press List 1, the screen focuses on List 1; I need the same for the rest of the options
This is the code for the example:
code
This behavior already exists in web pages but I haven't found this same behavior at the mobile app level. Thank you
Here is a small code snippet of something similar which might help you achieve you desired results.
By clicking the fab icon it will scroll down to item 35 within the ListView.
class MyHomePage extends StatelessWidget {
final _scrollController = ScrollController();
final _cardHeight = 200.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.orange,
onPressed: () => _animateToIndex(35),
child: Icon(Icons.add),
),
body: ListView.builder(
controller: _scrollController,
itemCount: 100,
itemBuilder: (_, i) => Container(
height: _cardHeight,
child: Card(
color: Colors.lightBlue,
child: Center(
child: Text("Scroll Item $i", style: TextStyle(fontSize: 28.0),),
),
),
),
),
);
}
_animateToIndex(index) {
_scrollController.animateTo(_cardHeight * index,
duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
}
}
You'll need to have a scrollable Widget (like ListView, SingleScrollableWidget) instead of a Column in ListSecondPage.
Then add a ScrollController to it and ListSecondPage should receive which button was tapped. Based on that selection you can scroll to the desired location with the ScrollController
I'm pretty new to flutter and i'm trying to do some animation on a PageView. to be precise, I want to animate removing an item.
I've tried serveral ways to animate it and apart from a solution, the way how you guys would solve such a problem would also be helpful for my flutter skils.
What I've tried so far:
Animating the padding and opacity
the problem with this is that when i set the padding in the setState in the onLongPress it rebuilds the widget and it overrides the padding again with the active or inactive CardPadding (i think)
Animating the width and height
I just can't seem to get both of these values to work
Animating the viewportFraction on the PageViewController
Would not know how to go about this and if it would be possible to do this only for a specific 'Page'
Below is the (stripped down) code I've written thus far.
class Main extends StatefulWidget {
#override
_MainState createState() => _MainState();
}
class _MainState extends State<Main> {
int activeCard = 0;
EdgeInsets inActiveCardPadding = EdgeInsets.symmetric(vertical: 120.0, horizontal: 20.0);
EdgeInsets activeCardPadding = EdgeInsets.symmetric(vertical: 105.0, horizontal: 10.0);
PageController pageController = PageController(
initialPage: 0,
viewportFraction: 0.8,
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: <Widget>[
PageView.builder(
itemCount: PlantCareApp.plants.length,
controller: pageController,
onPageChanged: (activeCardIndex) {
setState(() {
this.activeCard = activeCardIndex;
});
},
itemBuilder: (context, cardIndex) {
return AnimatedContainer(
padding: (activeCard == cardIndex) ? activeCardPadding : inActiveCardPadding;,
duration: Duration(milliseconds: 250),
child: PlantCard(
PlantCareApp.plants[cardIndex],
onTap: () {
Navigator.pushNamed(context, PlantDetailScreen.route, arguments: PlantCareApp.plants[cardIndex]);
},
onLongPress: () {
setState(() {
//
// ANIMATE OR TRIGGER ANIMATION HERE
//
// do the actual removing
/*
PlantCareApp.plants[cardIndex].remove(); // remove from db
PlantCareApp.plants.removeAt(cardIndex); // remove from List
*/
});
//PlantCareApp.plants[cardIndex].remove();
},
),
);
},
),
],
),
),
);
}
}
Any help will be greatly appreciated! How would you guys tackle a problem like this, or how would you tackle this specific use case.
I guess actually animating viewportFraction would be the nicest because of the adjecent 'Pages' moving toward each other as well?
Thanks!
I'm not certain if this is what you are looking for, but here goes.
One way of doing this is simply using the provided Widgets within Flutter. Two of these will help you out: AnimatedList and Dismissible.
Now, you could do something like this:
// define somewhere
final _animatedListGK = GlobalKey<AnimatedListState>();
// put in a function somewhere
return AnimatedList(
key: _animatedListGK,
padding: const EdgeInsets.all(0),
initialItemCount: PlantCareApp.plants.length,
itemBuilder: (context, index, animation) {
return FadeTransition(
opacity: animation,
child: _buildDismissibleRow(context, index, PlantCareApp.plants[index])
);
}
);
Note: you don't have to use the _animatedListGK global key per se, it depends on whether you can use AnimatedList.of(context) or not. Although it is the easier way.
The _animatedListGK is simply a Global Key that provides access to the AnimatedList so you can perform insertions/removals with animation.
Your dismissible row might look something like:
Widget _buildDismissibleRow(BuildContext context, int index, PlantModel plantModel) {
return Dismissible(
key: ValueKey<String>(plantModel.someKey),
direction: DismissDirection.startToEnd,
background: Container(color: Colors.red),
onDismissed: (direction) {
// You could use:
// AnimatedList.of(context)
_animatedListGK.currentState.removeItem(
index,
(context, animation) => Container(),
duration: Duration.zero
);
},
child: _buildContent(context, index, plantModel)
);
}
You could also do it without a dismissible row or even within the child of the dismissible row (_buildContent() for example). Something similar to:
// You could use:
// AnimatedList.of(context)
_animatedListGK.currentState.removeItem(
index,
(context, animation) {
return FadeTransition(
opacity: CurvedAnimation(parent: animation, curve: Interval(0.5, 1.0)),
child: SizeTransition(
sizeFactor: CurvedAnimation(parent: animation, curve: Interval(0.0, 1.0)),
child: _builContent(context, index, plantModel)
)
);
},
duration: const Duration(milliseconds: 300)
);
Notice how the SizeTransition simply "calls itself" by calling _builContent(context, index, plantModel)? That's how you can animate the row itself (out of existence).
Be sure to watch the videos in the aforementioned documentation pages! They will help understanding certain constructs.
A preview of what the dismissible might look like:
A preview of what the SizedTransition might look like: