I have a CupertinoPicker, on some condition I have to disable the CupertinoPicker.
Checked the CupertinoPicker class and didn't find any disable attribute in that.
If disabling is not possible, Can I Stop scroll on it?
CupertinoPicker(
backgroundColor: null,
itemExtent: PICKER_EXTENT,
useMagnifier: true,
looping: true,
onSelectedItemChanged: (int index) {
print('selected index $index');
},
children: List<Widget>.generate(dataList.length, (int index) {
return Center(
child: Text(dataList[index]),
);
}),
scrollController:
FixedExtentScrollController(initialItem: selectedIndex)),
You can use the AbsorbPointer to enable/disable touch events for any widget.
As per the documentation
When absorbing is true, this widget prevents its subtree from receiving pointer events by terminating hit testing at itself. It still consumes space during layout and paints its child as usual. It just prevents its children from being the target of located events, because it returns true from RenderBox.hitTest.
In your case, wrap the CupertinoPicker with an AbsorbPointer and use the absorbing property to enable/disable touch events for your CupertinoPicker
AbsorbPointer(
absorbing: true,
child: CupertinoPicker(
backgroundColor: null,
itemExtent: 100.0,
useMagnifier: true,
looping: true,
onSelectedItemChanged: (int index) {
print('selected index $index');
},
children: List<Widget>.generate(dataList.length, (int index) {
return Center(
child: Text(dataList[index]),
);
}),
scrollController:
FixedExtentScrollController(initialItem: selectedIndex)),
);
You can
Related
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.
I have a ScrollablePositionedList and I want to update the background color of every visible list item during a touch event. I am able to change the background color of the selected item in the list but finding it difficult to access/update every other item on list.
Here is what I have so far,
body: SizedBox(
height: 100,
child: WillPopScope(
onWillPop: () {
var position = itemPositionsListener.itemPositions.value.first.index;
print(position);
//trigger leaving and use own data
Navigator.pop(context, false);
//we need to return a future
return Future.value(false);
},
child: ScrollablePositionedList.builder(
scrollDirection: Axis.horizontal,
physics: NeverScrollableScrollPhysics(),
initialScrollIndex: startPosition,
itemScrollController: itemScrollController,
itemPositionsListener: itemPositionsListener,
itemCount: 14,
itemBuilder: (context, index) =>
MonthTile(getWidth(), months[index],(MonthTile tile){
itemScrollController.scrollTo(
index: getPosition(index),
duration: Duration(seconds: 1),
curve: Curves.easeInOutCubic);
// }
})))
),
What I'm basically aiming for is to have the selected item revert to its original color when another item is selected. I guess my question is, is there widget one can use to accomplish this?.
I am trying to get the drag offsets while using Dismissible widget. So tried to wrap it with GestureDetector but its onHorizontalDragStart is not working. Tried the either way ie by putting GestureDetector as child of Dismissible but then Dismissible stopped working.
How to solve this? Thnx...
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
var item = items[index];
return GestureDetector(
onHorizontalDragStart: (e) {
print(e);
},
child: Dismissible(
key: ValueKey(item),
background: Container(
color: Colors.teal,
),
child: ListTile(
title: Text("item $item"),
),
onDismissed: (d) {
items.remove(item);
},
),
);
},
);
Nested Gesture Widgets
The reason you are having this issue is because both of those widgets receive touch input and when you have two widgets that receive touch input, long story short the child wins that battle. Here is the long story. So both of your inputs from your Dismissible and GestureDetector are sent to what is called a GestureArena. There the arena takes into account multiple different factors but the end of the story is the child always wins. You can fix this issue by defining your own RawGestureDetector with its own GestureFactory which will change the way the arena performs.
ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
var item = items[index];
return Listener(
child: Dismissible(
key: ValueKey(item),
child: ListTile(
title: Text('This is some text'),
),
onDismissed: (DismissDirection direction) {
items.remove(index);
},
),
onPointerMove: (PointerMoveEvent move) {
if (move.localDelta.dx > 1) {//Ideally this number whould be
//(move.localDelta.dx != 0) but that is too sensitive so you can play with
//this number till you find something you like.
print(move.position);
}
},
);
},
);
I want to give all credit to Nash the author of Flutter Deep Dive: Gestures this is a great article I highly recommend you check it out.
This is just a simple todo list app. I want to implement a feature which lets me mark a task as complete when swiping right and delete the task when swiping left. This app uses a database and stores an added task in a list of type database (List<TodoItem> Itemlist= <TodoItem>[];).
Is it possible to use flutter_slidable?.
I have tried using Dismissible but i could only get it to delete the task.
List<TodoItem> Itemlist= <TodoItem>[];
SingleChildScrollView(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: Itemlist.length,
itemBuilder: (BuildContext context, int index){
String item = Itemlist[index].toString();
return Dismissible(
key: Key(UniqueKey().toString()),
onDismissed: (direction){
setState((){
deleteItem(Itemlist[index].id, index);
}
);
},
background: Container(
child: Icon(Icons.delete),
color: Colors.red,
alignment: Alignment.centerLeft,
),
child: Itemlist[index],
);
}
),
)
I want to get the same result as below but am unsure on how to pass the listview.builder in Slidable.
Wrap your tile widget with Slidable
Setup "actionPane" and "actions" parameters for Slidable
I have a NestedScrollView which works well to AutoHide the AppBar (one feature I want) when I use SliverAppBar. Where I am running into problems, is I use ListView.Builder as one of the body components downstream that I need apply its own ScrollController to (or seems I need to apply it here). This conflicts with the NestedScrollView and I lose the autohide of the appbar that is conveniently handled by the NestedScrollView and SliverAppBar.
If I attach the ScrollController on the NestedScrollView Then it only tracks scroll position up to an offset of 80.0 and after that, with a longer ListView I am unable to properly animateTo as I can with the ScrollController attached directly to the ListView.Builder.
Here is a snippet/sudo code of my implementation:
new Scaffold(
drawer: ...,
body: new NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
new SliverAppBar(
title: new Text('Title'),
floating: true,
snap: true
)
]
}
body: new Stack(
children: <Widget>[
new PageView(
children: <Widget>[
new PageView1(implements ListViewBuilder),
new PageView2(implements ListView),
new PageView3(implements ListView),
]
controller: _pageController,
),
new FloatingActionButton
]
)
)
)
class PageView1 extends StateFulWidget {
...//Builder return scrollable with max offset of 2000.0
return new ListView.builder(
itemBuilder: itemBuilder,
itemCount: objects.length,
controller: _scrollController,
);
...
#override
void initState{
scrollController = new scrollController();
scrollController.animateTo(800.0, ....);
}
}
The nice part about this is the PageView2 and 3 behave nicely, with the autohide of the app bar on scroll behavior as I am not creating ScrollControllers there. But, PageView1 behaves incorrectly and autoHide of the appbar breaks. But, I really want to be able to animateTo correctly and am unable to do so without placing the controller directly on the ListViewBuilder.
Any thoughts on a better implementation that would help me achieve this?
UPDATE: I have updated my implementation to more closely follow the NestedScrollView documentation. But, with no luck. It seems the addition of the ScrollController on the NestedScrollView only tracks the position of the SliverAppBar, and ScrollController.jumpTo or animateTo only jump to a maximum of the AppBar (offset 80)
I worked it out.. This is not how I expected it to work at all. I moved my SliverList into the headerSliverBuilder and it works the way I want it to. I took the cue to do so from this NestedScrollView example gist: https://gist.github.com/collinjackson/2bc6697d31e6b94ada330ef5e818a36f
Follow the NestedScrollViewExample:
Change your list view to SliverList or SliverFixedExtentList and Wrap it inside a safe area and a CustomScrollView:
return SafeArea(
top: false,
bottom: false,
child: Builder(builder: (BuildContext context) => CustomScrollView(
slivers: <Widget>[
return SliverFixedExtentList(
itemExtent: 100.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int i) => ChildWidget(items[i]),
childCount: items.length,
),
),
],
)),
);
I met with the same problem and here is my solution. You can add a key to NestedScrollView and use it to access the inner CustomScrollView/ListViewcontroller of the tab, you only need to add the ScrollController to NestedScrollView
final GlobalKey<NestedScrollViewState> documentsNestedKey = GlobalKey();
void initState() {
_scrollController = ScrollController();
_tabController = TabController(length: 2, vsync: this);
_tabController.addListener(() {
setState(() {});
});
// Tabs Pagination
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
documentsNestedKey.currentState!.innerController.addListener(() {
if (documentsNestedKey.currentState!.innerController.positions.last.atEdge) {
if (documentsNestedKey.currentState!.innerController.positions.last.pixels != 0) {
if (_tabController.index == 0) {
context.read<DeclarationsBloc>().add(const FetchDeclarationsEvent());
} else {
context.read<TasksBloc>().add(const FetchTasksEvent());
}
}
}
});
});
super.initState();
}
return SafeArea(
child: NestedScrollView(
key: documentsNestedKey,
controller: _scrollController,
floatHeaderSlivers: true,
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
backgroundColor: theme.background,
floating: true,
pinned: true,
forceElevated: innerBoxIsScrolled,
flexibleSpace: FlexibleSpaceBar(
background: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 11),
Padding(
padding: responsiveUtil.getHorizontalPadding(),
child: SearchWithFilter(
searchController: widget.searchController,
currentMenuIndex: 3,
onSearch: (text) {},
onSearchClear: () {},
onFilterTap: widget.onFilterTap,
),
),
],
),
),
bottom: DeclarationsTabBar(
tabController: _tabController,
),
),
),
],
body: TabBarView(
controller: _tabController,
children: [
MyDeclarationsTab(searchController: widget.searchController),
TasksTab(searchController: widget.searchController),
],
),
),
);