How can I do actions for long press & reorder on the same widget? - flutter

I am trying to implement a ReorderableListView that can also long-press for a context menu..
I have buildDefaultDragHandles: false, and am using ReorderableDelayedDragStartListener && a GestureDetector - GestureDetector because I need the Offset from the LongPress' details to know where the cursor was, and open the context menu at that location.
With the drag listener as parent of the GestureDetector the LongPress works but the drag doesn't.
With the GestureDetector as parent of the drag listener the drag works but the LongPress doesn't.
I want both to be able to trigger - LongPress to open the context menu, and if the user begins dragging it cancels that to perform the reorder.
In this example the only one that triggers for a LongPress is onLongPressDown, which actually triggers on regular interaction like tap/click rather than actual long presses.
class HomePage extends StatelessWidget {
HomePage({super.key});
final items = ['One', 'Two', 'Three', 'Four', 'Five'];
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: ReorderableListView.builder(
buildDefaultDragHandles: false,
itemBuilder: (context, index) {
return GestureDetector(
key: ValueKey(items[index]),
onLongPress: () => print('onLongPress'),
onLongPressCancel: () => print('onLongPressCancel'),
onLongPressDown: (details) => print('onLongPressDown'),
onLongPressEnd: (details) => print('onLongPressEnd'),
onLongPressMoveUpdate: (details) =>
print('onLongPressMoveUpdate'),
onLongPressStart: (details) => print('onLongPressStart'),
onLongPressUp: () => print('onLongPressUp'),
child: ReorderableDelayedDragStartListener(
index: index,
child: ListTile(
title: Text(items[index].toString()),
),
),
);
},
itemCount: items.length,
onReorder: (oldIndex, newIndex) {},
),
),
);
}
}

Related

Flutter Slidable - How to remove the slidable item from the list when i hit the delete button?

I have a simple list of ListTile s that I nested in Slidable widget using the flutter_slidable package. However there is one issue that when I use the delete button, the item stays on the screen, even though it is successfully removed from the list (If i make a hot reload, it will actually get removed from screen). I tried to add a setState method inside the onPressed function but I cannot add it because the SlidableAction widget is a stateless widget. How can I make the item disappear when I tap on this button?
Here is a small video demonstration. I delete two lists with the delete button. They stay on the screen. I go home screen and come back to see they got deleted.
https://streamable.com/td7blf
Here is my code:
Expanded(
child: ListView.builder(
itemCount: likedNames.length,
itemBuilder: (context, index) {
return Slidable(
key: const ValueKey(0),
endActionPane: ActionPane(
motion: ScrollMotion(),
dismissible: DismissiblePane(onDismissed: () {
likedNames.remove(likedNames[index]);
}),
children: [
SlidableAction(
onPressed: (context) {
likedNames.remove(likedNames[index]); // <- this is the part where I want to do the removing of the item
},
label: 'Delete',
backgroundColor: AppColors.error,
),
],
),
child: ListTile(
onTap: () {},
title: Text(likedNames[index].name),
trailing: Icon(Icons.chevron_right),
),
);
}),
),
onPressed: (context) {
setState() => likedNames.remove(likedNames[index])
},
I've used this solution:
onPressed: (BuildContext context) async {
await Provider.of<MyList>(context, listen: false)
.remove(MyList[index]);
}
MyList is a separate class:
class MyList extends ChangeNotifier
Whit this methods:
final List<String> _myList = [];
List<String> get items => _myList;
.
.
.
Future<bool> remove(String item) async {
_myList.remove(item);
notifyListeners();
return true;
}

flutter) I want to use a listview to jump directly to a specific section

As shown in the picture, I want to go directly to the corresponding list view when I press the button.
Instead of scrolling through the list, you can use the buttons to move left and right.
This is my current code.
As shown below, I am running a pageview called body (which changes briefly after using listview), and I know how to come out in order, but I don't know what to use to get it out of a specific number. Do you have a yes or another question?
GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Choice821()),);
},
2
class Choice821 extends StatelessWidget {
#override
Widget build(BuildContext context) {
QuestionController _controller = Get.put(QuestionController());
return Scaffold(
appBar: AppBar(
title: Text('복습 시험', style: TextStyle(color: Colors.black, fontWeight:FontWeight.bold,fontSize: 20,),),
centerTitle: true,
elevation: 0,
),
body: Body(),
);
}
}
2
child: PageView.builder(
physics: NeverScrollableScrollPhysics(),
controller: _questionController.pageController,
onPageChanged: _questionController.updateTheQnNum,
itemCount: _questionController.questions.length,
itemBuilder: (context, index) => ayotube(
question: _questionController.questions[index],
id: _questionController.questionNumber.value,
),
),
You can simply do this:
// jump to page index with animation
_questionController.pageController.animateToPage(index);
// or jump to page index without animation
_questionController.pageController.jumpToPage(index);

How do I use the offStage widget?

My first widget has a FlutterMap, As I navigate to another screen and come back for some reason the map will reload, so I want to hide it (maintain state), I tried using Visibility but it doesn't work and after some recommendation, I was told to use the OffStage widget but I have no clue on how to implement it.
Here is the logic, The first screen has a boolean check isVisible if it's true then the map will show on the screen, As I navigate away from the main screen then the boolean is set to false therefore the visibility is hidden. Again as I come back from the second screen the visibility is re set back to true hence showing the map. If I were to do the same thing using Offstage in place of Visibility how would I do it ?
class _MainScreenState extends State<MainScreen> {
bool isVisible= true;
Future testFunction(bool checkValue) async {
setState(() {
isVisible= checkValue;
});
#override
Widget build(BuildContext context) {
.....
Scaffold(
body: Container(
//change the margin
margin: EdgeInsets.fromLTRB(0, 0, 0, 300),
child:
Visibility(
visible: isVisible,
maintainAnimation: true,
maintainState: true,
child: (FlutterMap()
))
.........
GestureDetector(
onTap: () {
setState(() {
isVisible= !isVisible;
});
Navigator.push(
//send to search screen
context,
MaterialPageRoute(
builder: (context) => (SearchScreen(
testFunction: testFunction))));
The second page
class SearchScreen extends StatefulWidget {
final Function testFunction;
const SearchScreen({this.testFunction});
GestureDetector(
onTap: () {
Navigator.pop(
//send back data
context,
widget.testFunction(true));
},
child: Icon(Icons.arrow_back)),
If I were to do the same thing using Offstage in place of Visibility how would I do it?
Offstage works similar to Visibility, in the sense that they both have a boolean flag that you can use to toggle the action for each widget.
So to use an Offstage in place of a Visibility you can use:
Scaffold(
body: Container(
margin: EdgeInsets.fromLTRB(0, 0, 0, 300),
child: Offstage(
offstage: isVisible,
child: FlutterMap()
)

Can a Card widget display a ListTile?

In this app, when the user clicks the FAB, it returns a Card wrapped in a GestureDetector. when the user clicks on a displayed card, the GestureDetector will navigate them to another page. I want to implement a delete function so that i can dismiss a Card.
So i defined the child: of the Card as a ListTile with a trailing Icon which is supposed to delete that particular Card. But when i add a Card, it only displays the title: and does not display the trailing Icon.
The Cards are displayed in a SliverGrid with a crossAxisCount = 2.
question: do Card widgets support displaying a ListTile with a trailing widget or is my implementation wrong?
P.S. i have tried setting crossAxisCount = 1 but it still does not show the trailing Icon.
card generating function:
void addItems() async {
setState(() {
cardList.insert(0, new GestureDetector(
onTap: () async {
await Navigator.push(context, MaterialPageRoute(
builder: (context) =>
TodoList(), // this just navigates to another screen ; not important in this question
)
);
},
child: Card(
child: ListTile(
title: Text("project 1"),
trailing: new Icon(Icons.remove_circle,
color: Colors.redAccent,),
// subtitle: whitefontstylemont(text: "project 1", size: 20,)) //this is just a custom TextStyle
),
)
));
});
}
card deleting function:
_deleteNoDo(int index) async {
debugPrint("Deleted Item!");
setState(() {
cardList.removeAt(index);
});
}
complete example (excluding above mentioned functions):
class _Starting_screenState extends State<Starting_screen> {
int _count = 0;
#override
Widget build(BuildContext context) {
List<Widget> cardList = new List.generate(
_count, (int i) => new createCard());
SystemChrome.setEnabledSystemUIOverlays([]);
// _deleteNoDo(int index) async {
// debugPrint("Deleted Item!");
// setState(() {
// cardList.removeAt(index);
// });
// }
//
// void addItems() async {
// setState(() {
// cardList.insert(0, new GestureDetector(
// onTap: () async {
// await Navigator.push(context, MaterialPageRoute(
// builder: (context) =>
// TodoList(), // this just navigates to another screen ; not important in this question
// )
// );
// },
// child: Card(
// child: ListTile(
// title: Text("project 1"),
// trailing: new Icon(Icons.remove_circle,
// color: Colors.redAccent,),
//// subtitle: whitefontstylemont(text: "project 1", size: 20,)) //this is just a custom TextStyle
// ),
// )
// ));
// });
// }
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
setState(() {
_count += 1;
});
},
heroTag: "btn2",
child: Icon(Icons.add, color: Color(whitecolor),), // this is just a custom color
backgroundColor: Color(redcolor),), // this is just a custom color
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
flexibleSpace: FlexibleSpaceBar(
),
actions: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 20, right: 10),
child: whitefontstyle(text: "Remaining tasks for today - ${cardList.length}", size: 20,), // this is just a custom textstyle
),
),
],
),
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2
),
delegate: new SliverChildBuilderDelegate((context,
index) {
return cardList[index]; // this is where the cards are displayed in a list
},
childCount: cardList.length
)
),
]
)
);
}
}
actual result:
expected result (assume only displaying title and trailing icon as shown below):
Are you sure that you are calling the right methods. Cause there are few things which are not in place or you haven't shared the right code. I will address them below.
First of all the alignment of child text in card. It is in center of the card but there is no center property used in your code. Card widget does not auto align the text.
Second adding the items. You post the code of addItems function but also added another function in build with name createCard. So we don't know if that createCard have the same widget tree as the addItems function or not.
Its not working not so you are not seeing it but even if you are able to see the Icon widget you still won't be able to delete the item. The reason being is its a non-clickable widget. You should be using the IconButton widget if you want to add the click functionality on it.
These are my finding in your code. If you can review them or either share the correct code, may be I am able to help more.

Create a new Card using onPressed()

When the user clicks the FAB, i want it to create a new Card widget wrapped in a GestureDetector. I have created a list of type widget and also created a function with a setState to add to the list and display it in a SliverGrid. but when i click the FAB, it doesnt seem to be adding.
Could i get a small guide on how to add a Card using an FAB. (or adding any type of widget in a SliverGrid).
the list : List<Widget> cardList = <Widget>[];
function for adding items to the list :
void addItems() async{
setState(() {
cardList.insert(0, GestureDetector(
onTap: () async {
await Navigator.push( context, MaterialPageRoute(
builder: (context) => TodoList(), // this just navigates to another screen ; not important in this question
)
);
},
child: Card(
child:Center(child: whitefontstylemont(text: "project 1", size:20,)) //this is just a custom TextStyle
),
));
});
}
the floating action button which adds a new Card to the list :
floatingActionButton: FloatingActionButton( // this is inside a Scaffold
onPressed: ()async {
setState(() {
addItems();
});
},
heroTag: "btn2",
child: Icon(Icons.add, color: Color(whitecolor),), backgroundColor: Color(redcolor),),
the SliverGrid where i want a new card to be displayed :
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2
),
delegate: new SliverChildBuilderDelegate((context, index) {
return cardList[index];
},
childCount: cardList.length // this is where i want a new Card to be displayed
)
),