I'm trying to build a list that when the user swipes the list item from left to right a slidable pan shows up and when the user swipes from right to left, the list item text style changes (adding an underline and changing color).
I can't combine these two functionalities.
I've tried to combine dissmisable with slidable but somhow dissmisable overrides slidable.
Just Slidable code (works good):
ListView(
children: [
Slidable(
key: const ValueKey(0),
endActionPane: ActionPane(
extentRatio: 0.25,
motion: const StretchMotion(),
children: [
SlidableAction(
onPressed: (context) {},
foregroundColor: Colors.black,
icon: Icons.edit,
),
SlidableAction(
onPressed: (context) {},
foregroundColor: Colors.red,
icon: Icons.delete,
),
],
),
child: const ListTile(
title: Text('Sample Task'),
),
),
],
),
Slidable + Dismissable (doesn't behave ok):
ListView(
children: [
Slidable(
key: const ValueKey(0),
endActionPane: ActionPane(
extentRatio: 0.25,
motion: const StretchMotion(),
children: [
SlidableAction(
onPressed: (context) {},
foregroundColor: Colors.black,
icon: Icons.edit,
),
SlidableAction(
onPressed: (context) {},
foregroundColor: Colors.red,
icon: Icons.delete,
),
],
),
child: Dismissible(
key: const ValueKey(0),
child: const ListTile(
title: Text('Sample Task'),
),
),
),
],
),
GestureDetector(
onHorizontalDragUpdate: (details) {
int sensitivity = 10;
if (details.delta.dx > sensitivity) {
setState(() {
result = "swiped right direction";
});
} else if (details.delta.dx < -sensitivity) {
setState(() {
result = "swiped left direction";
});
}
},
child: const ListTile(
title: Text('Sample Task'),
),
),
you can write these codes.
Related
How I can make side bar upon button to add the coin.
you can achieve that by using the Dismissible widget
here's a video on how to use it
https://www.youtube.com/watch?v=iEMgjrfuc58
Use This Package:
flutter_slidable: ^2.0.0
Scaffold(
body: ListView(
children: [
Slidable(
// Specify a key if the Slidable is dismissible.
key: const ValueKey(0),
// The start action pane is the one at the left or the top side.
startActionPane: ActionPane(
// A motion is a widget used to control how the pane animates.
motion: const ScrollMotion(),
// A pane can dismiss the Slidable.
dismissible: DismissiblePane(onDismissed: () {}),
// All actions are defined in the children parameter.
children: const [
// A SlidableAction can have an icon and/or a label.
SlidableAction(
onPressed: doNothing,
backgroundColor: Color(0xFFFE4A49),
foregroundColor: Colors.white,
icon: Icons.delete,
label: 'Delete',
),
SlidableAction(
onPressed: doNothing,
backgroundColor: Color(0xFF21B7CA),
foregroundColor: Colors.white,
icon: Icons.share,
label: 'Share',
),
],
),
// The end action pane is the one at the right or the bottom side.
endActionPane: const ActionPane(
motion: ScrollMotion(),
children: [
SlidableAction(
// An action can be bigger than the others.
flex: 2,
onPressed: doNothing,
backgroundColor: Color(0xFF7BC043),
foregroundColor: Colors.white,
icon: Icons.archive,
label: 'Archive',
),
SlidableAction(
onPressed: doNothing,
backgroundColor: Color(0xFF0392CF),
foregroundColor: Colors.white,
icon: Icons.save,
label: 'Save',
),
],
),
// The child of the Slidable is what the user sees when the
// component is not dragged.
child: const ListTile(title: Text('Slide me')),
),
Slidable(
// Specify a key if the Slidable is dismissible.
key: const ValueKey(1),
// The start action pane is the one at the left or the top side.
startActionPane: const ActionPane(
// A motion is a widget used to control how the pane animates.
motion: ScrollMotion(),
// All actions are defined in the children parameter.
children: [
// A SlidableAction can have an icon and/or a label.
SlidableAction(
onPressed: doNothing,
backgroundColor: Color(0xFFFE4A49),
foregroundColor: Colors.white,
icon: Icons.delete,
label: 'Delete',
),
SlidableAction(
onPressed: doNothing,
backgroundColor: Color(0xFF21B7CA),
foregroundColor: Colors.white,
icon: Icons.share,
label: 'Share',
),
],
),
// The end action pane is the one at the right or the bottom side.
endActionPane: ActionPane(
motion: const ScrollMotion(),
dismissible: DismissiblePane(onDismissed: () {}),
children: const [
SlidableAction(
// An action can be bigger than the others.
flex: 2,
onPressed: doNothing,
backgroundColor: Color(0xFF7BC043),
foregroundColor: Colors.white,
icon: Icons.archive,
label: 'Archive',
),
SlidableAction(
onPressed: doNothing,
backgroundColor: Color(0xFF0392CF),
foregroundColor: Colors.white,
icon: Icons.save,
label: 'Save',
),
],
),
// The child of the Slidable is what the user sees when the
// component is not dragged.
child: const ListTile(title: Text('Slide me')),
),
],
),
),
);
I'm working on an app that takes the user through four different steps, with each step requiring the user to provide some information. I'm using the Stepper, and I'd like to replace the last step buttons with a custom button. How can I go about doing that?
There is no property to set / change the button in Stepper Widget
You can use enhance_stepper package to modify.
Widget buildStepperCustom(BuildContext context) {
return EnhanceStepper(
stepIconSize: 30,
type: _type,
horizontalTitlePosition: HorizontalTitlePosition.bottom,
horizontalLinePosition: HorizontalLinePosition.top,
currentStep: _index,
physics: ClampingScrollPhysics(),
steps: tuples.map((e) => EnhanceStep(
icon: Icon(e.item1, color: Colors.blue, size: 30,),
state: StepState.values[tuples.indexOf(e)],
isActive: _index == tuples.indexOf(e),
title: Text("step ${tuples.indexOf(e)}"),
subtitle: Text("${e.item2.toString().split(".").last}",),
content: Text("Content for Step ${tuples.indexOf(e)}"),
)).toList(),
onStepCancel: () {
go(-1);
},
onStepContinue: () {
go(1);
},
onStepTapped: (index) {
ddlog(index);
setState(() {
_index = index;
});
},
controlsBuilder: (BuildContext context, { VoidCallback? onStepContinue, VoidCallback? onStepCancel }){
return Row(
children: [
SizedBox(height: 30,),
ElevatedButton(
onPressed: onStepContinue,
child: Text("Next"),
),
SizedBox(width: 8,),
TextButton(
onPressed: onStepCancel,
child: Text("Back"),
),
],
);
}
);
I used a boolean as lastField set to false.
In the step continue and step cancel, I checked to see if the current step was the last one and change the lastField appropriately.
Then you can change the controls builder based on the lastField.
So my on step continue would look like this:
onStepContinue: (){
if(forms[_currentStep].currentState!.validate()){
_currentStep < 3 ?
setState((){
_currentStep += 1;
if(_currentStep == 3){
lastField = true;
}else{
lastField = false;
}
}): null;
}
},
And my controls builder would look like this:
controlsBuilder: (context, details){
return Padding(
padding: const EdgeInsets.all(8.0),
child: lastField ? Row(
children: [
ElevatedButton(onPressed: (){}, child: const Text("Submit"), style: ElevatedButton.styleFrom(primary: buttonPurple, shape: const StadiumBorder()),),
TextButton(onPressed: details.onStepCancel, child: const Text("Cancel", style: TextStyle(color: Colors.white70),))
],
) : Row(
children: [
ElevatedButton(onPressed: details.onStepContinue, child: const Text("Next"), style: ElevatedButton.styleFrom(primary: buttonPurple, shape: const StadiumBorder()),),
TextButton(onPressed: details.onStepCancel, child: const Text("Cancel", style: TextStyle(color: Colors.white70),))
],
)
);
},
Here's how I went about it in my controlsBuilder.
controlsBuilder: (BuildContext context, ControlsDetails controls) {
final isLastStep = _activeStepIndex == stepList().length - 1;
return Row(
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(60)),
onPressed: controls.onStepContinue,
child: (isLastStep)
? TextButton(
style: TextButton.styleFrom(
minimumSize: const Size.fromHeight(60)),
onPressed: () {
setState(() {
sending = true;
});
sendData();
},
child: const Text('Submit',
style: TextStyle(color: Colors.white)),
)
: const Text('Next'),
),
),
I am using Default Sidebar but getting error while scroll
'The $controllerForError is currently attached to more than one '
'ScrollPosition.',
here is Sidebar Widget
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
DrawerHeader(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/bgimage.jpg"),
fit: BoxFit.cover),
color: Colors.blue,
),
child: Center(
child: Column(
children: [
Text(
'${store.user['name']}',
style: TextStyle(color: Colors.white, fontSize: 25),
),
Lottie.asset(
'assets/lottie/coin.json',
width: 50,
height: 50,
),
Text(
'${store.user['wallet']}',
style: TextStyle(color: Colors.white, fontSize: 20),
),
],
)),
),
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
onTap: () {
Navigator.popAndPushNamed(context, '/home');
},
),
ListTile(
leading: Icon(Icons.assessment_outlined),
title: Text('Loss/Profit'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.calendar_today),
title: Text('Results'),
onTap: () {
Navigator.popAndPushNamed(context, '/result');
},
),
ListTile(
leading: Icon(Icons.supervised_user_circle),
title: Text('Referrals'),
onTap: () {
Navigator.pop(context);
},
),
],
);
}
}
Using in homepage
drawer: Drawer(
child: Sidebar(),
),
where is scroll position is using multiple
by default drawer is already scrollable. i think the cause of not being able to scroll is not in the drawer
everyone
I'm encountering a problem while using Flutter.
Basically when i open my CustomDrawer widget, not always but quite frequently, the keyboard pops out in an unwanted way.
I don't get why it does it... maybe because it re-runs the build method or something i don't know. Down below you can find the code.
Every little bit of information is well appreciated.
Thanks everyone.
Here's the Screen.dart
...
build(context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
drawer: CustomDrawer(),
...
And the custom_drawer_widget.dart
...
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
Stack(
children: [
Container(
padding: EdgeInsets.all(20),
child: Text(
"Hi, $username",
style: TextStyle(
fontSize: 22,
),
),
alignment: Alignment.bottomLeft,
color: Colors.yellow,
height: 300,
),
],
),
Container(
height: 60.0 * 6,
child: Column(
children: [
Container(
height: 60,
child: FlatButton(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
highlightColor: Colors.grey[350],
color: Colors.transparent,
onPressed: () {},
child: Row(
children: [
Icon(
Icons.home,
color: Colors.grey[600],
),
SizedBox(width: 30),
Text("Homepage"),
],
),
),
),
ListTile(
leading: Icon(Icons.book),
title: Text("Diary"),
onTap: () {}),
ListTile(
leading: Icon(Icons.chat),
title: Text("Chat"),
onTap: () {}),
ListTile(
leading: Icon(Icons.credit_card),
title: Text("Credit Card"),
onTap: () {}),
ListTile(
leading: Icon(Icons.exit_to_app),
title: Text("Sign Out"),
onTap: () async {
await FirebaseAuth.instance.signOut();
}),
ListTile(
leading: Icon(Icons.settings),
title: Text("Settings"),
onTap: () {}),
],
),
),
Expanded(
child: Container(
padding: EdgeInsets.all(22),
alignment: Alignment.bottomLeft,
child: Row(
children: [
Text("Version 0.1"),
],
),
),
)
],
),
);
}
...
I don't see exactly where you dismiss the keyboard, but I was having the same issue after dismissing the keyboard after a form submit. This fixed my issue.
See this answer here:
https://github.com/flutter/flutter/issues/54277
Where instead of:
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
The code should be:
onTap: () {
final FocusScopeNode currentScope = FocusScope.of(context);
if (!currentScope.hasPrimaryFocus && currentScope.hasFocus) {
FocusManager.instance.primaryFocus.unfocus();
}
},
I have navigation drawer, i select one item from it and after i select second item when i pressed back after selecting second item app got closed i need to go on first item when i pressed back .
#override
Widget build(BuildContext context) {
List<Widget> drawerOptions = [];
for (var i = 0; i < drawerItems.length; i++) {
var d = drawerItems[i];
drawerOptions.add(new ListTile(
leading: new Icon(d.icon),
title: new Text(
d.title,
style: new TextStyle(fontSize: 14.0, fontWeight: FontWeight.w400),
),
selected: i == _selectedIndex,
onTap: () => _onSelectItem(i),
));
}
return new Scaffold(
appBar: SearchBar(
loader: QuerySetLoader<ProductModel>(
querySetCall: _getItemListForQuery,
itemBuilder: _buildItemWidget,
loadOnEachChange: true,
animateChanges: true,
),
defaultBar: AppBar(
title: Text('Home'),
),
),
drawer: new Drawer(
child: SingleChildScrollView(
child: new Column(
children: <Widget>[
DecoratedBox(
position: DecorationPosition.background,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/bac_image.png'),
fit: BoxFit.cover),
),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20.0),
child: new Image(
image: new AssetImage("assets/blik_mobile.png"),
height: 100,
width: 100,
),
),
Column(children: drawerOptions)
],
),
)
],
),
),
),
body: _getDrawerItemScreen(_selectedIndex),
bottomNavigationBar: BottomNavigationBar(
onTap: (int index) {
setState(() {
this.index = index;
// Navigator.of(context).pop();
});
_navigateToScreens(index);
},
type: BottomNavigationBarType.fixed,
currentIndex: index,
items: [
BottomNavigationBarItem(
title: Text('Home'),
icon: Icon(
Icons.home,
color: Colors.black,
)),
BottomNavigationBarItem(
title: Text('Categories'),
icon: Icon(Icons.dashboard, color: Colors.black)),
BottomNavigationBarItem(
title: Text('Cart'),
icon: Icon(Icons.shopping_cart, color: Colors.black)),
BottomNavigationBarItem(
title: Text('WishList'),
icon: Icon(Icons.favorite, color: Colors.black)),
BottomNavigationBarItem(
title: Text('Profile'),
icon: Icon(Icons.person, color: Colors.black)),
],
),
);
}
on select item of drawer
_onSelectItem(int index) {
setState(() {
_selectedIndex = index;
_getDrawerItemScreen(_selectedIndex);
});
Navigator.of(context).pop();
}
get selected drawer screen method is here
_getDrawerItemScreen(int pos) {
switch (pos) {
case 0:
return new FirstScreen(drawerItem: drawerItems[_selectedIndex]);
case 1:
return new OrderHistory(drawerItem: drawerItems[_selectedIndex]);
case 2:
return new WalletScreen(
drawerItem: drawerItems[_selectedIndex],
);
case 3:
return new AddressList(drawerItem: drawerItems[_selectedIndex]);
case 5:
return new AboutUs(drawerItem: drawerItems[_selectedIndex]);
// return new AddAddress(drawerItem: drawerItems[_selectedIndex],);
default:
return new FirstScreen(drawerItem: drawerItems[_selectedIndex]);
}
}
i want to handle back after selecting any of item from my navigation drawer .
If you need to control screen stack, you can use
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
}
detail reference https://medium.com/flutter-community/flutter-push-pop-push-1bb718b13c31
If you want to handle back button
you can wrap your Scaffold with WillPopScope
return WillPopScope(
onWillPop: () => _exitApp(context),
child: Scaffold(
appBar: AppBar(
title: Text("Navigation Demo"),
and ask user Do you want to exit this application or current screen
Future<bool> _exitApp(BuildContext context) {
return showDialog(
context: context,
child: AlertDialog(
title: Text('Do you want to exit this application?'),
content: Text('We hate to see you leave...'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('No'),
),
FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('Yes'),
),
],
),
) ??
false;
}
detail reference https://codingwithjoe.com/flutter-navigation-how-to-prevent-navigation/