Custom Dialog Flutter with many buttons - android

What I'm trying to do is create a custom dialog with many buttons in flutter,
When user press a button, my goal is to close custom dialog and know which button is pressed (in order to refresh my homepage by using of provider)
I define custom dialog with 2 button (for example). How I can achieve my goal?
That is the code:
CustomDialog.dart
import 'package:flutter/material.dart';
class CustomDialog extends StatelessWidget {
dialogContent(BuildContext context) {
return Container(
decoration: new BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10.0,
offset: const Offset(0.0, 10.0),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min, // To make the card compact
children: <Widget>[
RaisedButton(
onPressed: (){},
child: Text("Button 1"),
),
RaisedButton(
onPressed: (){},
child: Text("Button 2"),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 0.0,
backgroundColor: Colors.transparent,
child: dialogContent(context),
);
}
}
In main.dart I call it:
Container(
child: Center(
child: RaisedButton(
onPressed: (){
showDialog(context: context,
builder: (BuildContext context){
return CustomDialog(
);
}
);
},
child: Text("Custom Dialog"),
),
),
),

I solved in this way:
showDialog(context: context,
builder: (BuildContext context){
return CustomDialog(
);
}
).then((value) {
});
And in CustomDialog:
Navigator.pop(context, //** RETURNED VALUE**//);

From the dialog it is possible to return the value back to the place where it was opened
First, you have to wait for the value when you open the dialog
return Container(
child: Center(
child: RaisedButton(
onPressed: () async {
var pressedButtonNumber = await showDialog<int>(
context: context,
builder: (BuildContext context) {
return CustomDialog();
});
print(pressedButtonNumber);
},
child: Text("Custom Dialog"),
),
),
);
}
And then you have to return the value when you close the dialog
RaisedButton(
onPressed: () {
Navigator.of(context).pop(1);
},
child: Text("Button 1"),
),

Related

How to get Dart Flutter floatActionButton bottom right?

I have a screen like this:
I'm trying to place the floatActionButton at the bottom right, but I couldn't.I'm putting the floatActionButton, but I couldn't put it in the bottom right.
How can I put it in the bottom right?
Codes:
Container(
child: Padding(
padding: EdgeInsets.only(left: 8, right: 8, bottom: 40),
child: Column(
children: [
SizedBox(height: 15,),
Text("Profile", style: TextStyle(fontSize: 27),),
Divider(thickness: 1, color: Colors.black,),
SizedBox(height: 5),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Solved Tests:",style: TextStyle(fontSize: 19)),
],
),
SizedBox(height: 20,),
Container(
width: double.infinity,
height: 200,
child: Expanded(
child: FutureBuilder(
future: listUpload(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
late List<String?> items;
if (!snapshot.hasData){
return Text("Not found");
}
if (snapshot.connectionState == ConnectionState.waiting) {
items = [];
} else if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
items = snapshot.data as List<String?>;
} else {
items = [];
}
return Scrollbar(
isAlwaysShown: true,
controller: _scrollContreller,
scrollbarOrientation: ScrollbarOrientation.right,
child: ListView.builder(
controller: _scrollContreller,
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(bottom: 20, left: 10, right: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
child: ListTile(
title: Text(
items[index].toString(),
style: TextStyle(fontSize: 20),
),
),
),
);
},
),
);
})),
),
Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox(height: 15,),
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Container(
width: 250,
height: 40,
child: GFButton(
text: "Temizle", textStyle: TextStyle(fontSize: 20, color: Colors.red),
color: Colors.red,
type: GFButtonType.outline2x,
onPressed: () {
AlertDialog eminlik = AlertDialog(
title: Text("Onay"),
content: Text("Çözdüğünüz testlerin kayıtları silinecektir. Emin misiniz?"),
actions: [
FlatButton(
child: Text("Evet"),
onPressed: () {
Navigator.pop(context);
setState(() {
eminlikSil();
});
},
),
FlatButton(
child: Text("Hayır"),
onPressed: () {
Navigator.pop(context);
},
)
],
);
showDialog(context: context, builder: (context) => eminlik);
},
),
),
),
],
),
),
// !!!!!!!!!!! <<<<<<<<<
// !!!!!!!!!!! <<<<<<<<<
FloatingActionButton(
child: Text("FAB"),
onPressed: () {}),
],
),
),
),
In the code I provided, I commented out where I put the floatActionButton.I put the codes of the screen in the picture directly. floatActionButton codes are below.
Thanks in advance for the help.
Looks like you want the achieve the default material design behaviour, in that case use a Scaffold and set the floatingActionButton value
return Scaffold(
body: , // paste your page content here
floatingActionButton: FloatingActionButton(
child: Text("FAB"),
onPressed: () {},
),
);
Your floating action button will placed at the bottom because it's the default behaviour, if you want to change that play with the value of floatingActionButtonLocation
if you don't want to relay on Scaffold use a Stack widget instead
return Stack(
children: [
// paste your page content here
Align(
alignment: Alignment.bottomRight,
child: FloatingActionButton(
child: Text("FAB"),
onPressed: () {},
),
)
],
);
You just need to put the floatingActionButton as a named parameter of Scaffold.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Floating Action Button'),
),
body: const Center(child: Text('Press the button below!')),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
backgroundColor: Colors.green,
child: const Icon(Icons.add),
),
);
}
Reference: official flutter documentation
The easiest way to add a FloatingActionButton is to add it directly to your Scaffold.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
Another solution would be to encapsulate your view in a Stack and to add your FloatingActionButton in an Align.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
Container(),
Align(
alignment: Alignment.bottomRight,
child: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
)
],)
);
}
Well, if you're using Scaffold, it has floatingActionButton parameter. Where you should put your float, and if it doesn't place it to position you want you can correct it with floatingActionButtonLocation parameter.
Example:
Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
)
Otherwise, you can wrap FloatActionButton with Align widget. And give it alignment as you want.
You can set Alignment for use Container or Align Widget .
Using Container:
Container(
alignment: Alignment.bottomRight,
child: FloatingActionButton(
onPressed: () {},
child: Icon(
Icons.add,
),
),
),
Using Align:
Align(
alignment: Alignment.bottomRight,
child: Container(
child: FloatingActionButton(
hoverColor: Colors.black,
elevation: 10,
onPressed: () {},
child: Icon(
Icons.add,
),
),
),
),
Using Scaffold:
Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
);
Result Screen->

Flutter DragTarget onAccept is not being called

I am developing a feature where the user enters a sentence, in the next screen the words of that sentence get shuffled randomly, then the user has to drag the words to a drag target to form the original sentence.
You can get an idea from the screenshots below.
First screen
Second screen
Now the problem I am having is, when dragging the words to the target I can see the DragTarget is calling onWillAccept as I added a print() statement there, if it is doing so then it should call onAccept eventually but it is not doing so. This is why my codes that deal with Bloc are not getting called and the words are not showing up in the target spot.
Code
class SentenceMakeScreen extends StatefulWidget {
String inputSentence;
SentenceMakeScreen(this.inputSentence);
#override
State<SentenceMakeScreen> createState() => _SentenceMakeScreenState();
}
class _SentenceMakeScreenState extends State<SentenceMakeScreen> {
List<String> sentence = [];
List<Widget> wordWidgets = [];
bool isDragSuccessful = false;
final ButtonStyle _buttonStyle = ElevatedButton.styleFrom(
textStyle: TextStyle(fontSize: 20)
);
_getTextWidgets(List<String> sentence) {
for(var i = 0; i < sentence.length; i++){
wordWidgets.add(
Draggable<WordWidget>(
data: WordWidget(sentence[i]),
child: WordWidget(sentence[i]),
feedback: WordWidget(sentence[i]),
childWhenDragging: Container(),
)
);
}
}
_randomlyOrganizeSentence(String inputString) {
sentence = inputString.split(new RegExp(r" "));
sentence.shuffle();
print(sentence);
}
#override
void initState() {
// TODO: implement initState
_randomlyOrganizeSentence(widget.inputSentence);
_getTextWidgets(sentence);
super.initState();
}
#override
Widget build(BuildContext context) {
final _dragDropBloc = DragDropBloc();
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.pop(context);
},
),
),
body: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
DragTarget<WordWidget>(
builder: (context, data, rejectedData) {
return Center(
child: this.isDragSuccessful
?
Container(
width: double.maxFinite,
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(width: 1.0, color: Colors.black),
),
),
child: StreamBuilder<List<WordWidget>>(
stream: _dragDropBloc.widgetStream,
initialData: [],
builder: (BuildContext context, AsyncSnapshot<List<WordWidget>> snapshot) {
print("Here ${snapshot.data}");
return Wrap(
direction: Axis.horizontal,
children: [
//correctly ordered words
],
);
},
),
)
:
Container(
width: double.maxFinite,
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(width: 1.0, color: Colors.black),
),
),
child: Text("Drag here")
),
);
},
onWillAccept: (data) {
print("true");
return true;
},
onAccept: (data) {
print(data.toString());
_dragDropBloc.dragDropEventSink.add(
DropEvent(WordWidget(data.toString()))
);
setState(() {
this.isDragSuccessful = true;
//draggedWords.add(data.toString());
});
},
),
Wrap(
direction: Axis.horizontal,
children: wordWidgets
),
Container(
child: ElevatedButton(
style: _buttonStyle,
onPressed: () {
},
child: Text("Check"),
),
),
],
),
),
);
}
}
WordWidget
import 'package:flutter/material.dart';
class WordWidget extends StatelessWidget {
final String word;
const WordWidget(this.word);
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.red[900],
border: Border.all(
width: 4,
color: Colors.black
),
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: EdgeInsets.all(5),
child: Text(
word,
style: TextStyle(
color: Colors.white
),
)
),
);
}
}
I tried adding the type of data I am passing from Draggable to DragTarget, this is what was advised here. It did not work.
I was also getting the same error earlier today. I then upgraded my flutter to the latest version and wrote the DragTarget code again from scratch. I don't know what worked for me but you can try doing the same.

User input number dialog box in flutter

I'm a new flutter user. I'm trying to make a shopping list app. Basically the first page contains 3 clickable images. These images are the logos of the three stores we currently shop at for our supplies. When you click on the image, it takes you to another page that has a grid view of all of the items (pictures of the items) that we purchase from that store. My end goal is that when you click on the picture of the item, a dialog box pops up asking how many of this item you would like to put on the list, then put it on the list. I've gotten as far as creating a grid view of the items, and making the first image clickable, but I'm not sure how to make the input dialog box. Any help would be greatly appreciated.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Navigation Basics',
home: FirstRoute(),
));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'Shopping List');
}
}
class FirstRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("Pick a store to start your list"),
),
body: Column(
children: [
Center(
child: Container(
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 150, maxWidth: 150),
child: Ink.image(
image: AssetImage('images/aldi.jpg'),
fit: BoxFit.fitWidth,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondRoute()),
);
},
),
),
),
),
),
Expanded(
child: Container(
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 150, maxWidth: 150),
child: Ink.image(
image: AssetImage('images/rd.png'),
fit: BoxFit.fitWidth,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ThirdRoute()),
);
},
),
),
),
),
),
Expanded(
child: Container(
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 150, maxWidth: 150),
child: Ink.image(
image: AssetImage('images/sams.jpg'),
fit: BoxFit.fitWidth,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FourthRoute()),
);
},
),
),
),
),
),
],
),
),
);
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Aldi's"),
),
body: Container(
padding: EdgeInsets.all(8.0),
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 8.0,
mainAxisSpacing: 8.0,
children: <Widget>[
FlatButton(onPressed: (),
child: Image.asset('images/2milk.jpg'),
),
Image.asset('images/skimMilk.jpg'),
Image.asset('images/almondMilk.jpg'),
Image.asset('images/coconutMilk.jpg'),
Image.asset('images/soyMilk.jpg'),
Image.asset('images/halfAndHalf.jpg'),
Image.asset('images/heavyCream.jpg'),
Image.asset('images/whipCream.jpg'),
],
),
),
),
);
}
}
class ThirdRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Restraurant Depot"),
),
body: Center(),
);
}
}
class FourthRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sam's Club"),
),
body: Center(),
);
}
}
To make a dialog box, there are many widgets you can use.
I think the best use case here is an AlertDialog with your widgets items like TextField on top.
Inside the onTap add this, you can leave the long decorations if you don't want a frame-less border, i just added them to make my TextField look neater. You can also leave the controller, if you do not plan using a TextEditingController.
onTap: () {
var alert = AlertDialog(
title: Text("How many items do you need?"),
content: TextField(
style: TextStyle(
decoration: TextDecoration.none),
maxLines: 1,
maxLengthEnforced: false,
autofocus: false,
enabled: true,
onSubmitted: (String text) {
int number_input = int.parse(text);
// Do something with your number like pass it to the next material page route
},
controller: _controller,
decoration: new InputDecoration(
errorStyle: TextStyle(color: Colors.redAccent),
border: new UnderlineInputBorder(
borderSide: BorderSide(
color: Color.fromRGBO(40, 40, 40, 1.0),),
borderRadius: BorderRadius.circular(10.0),),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Color.fromRGBO(40, 40, 40, 1.0),),
borderRadius: BorderRadius.circular(10.0),),
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Color.fromRGBO(40, 40, 40, 1.0),),
borderRadius: BorderRadius.circular(10.0),),
prefixIcon: new Icon(
Icons.playlist_add,
size: 18.0,),),),);
showDialog(
context: context,
builder: (context) {
return alert;
},);
},
Add You Trigger OnTap() on your image and then use this code to pop your Custom dialog by create your widget in Container
showDialog(
context: context,
builder: (_) => Container(
child: //add you custom widget here
));

While upgrading to dart 2.2.1 on pressed is not working in icon button and raised button

If I pressed a button and open a empty page in flutter which will be locating in the same page but the on pressed is not working
Widget _cameraDisable(){
return Container(
alignment: Alignment.bottomRight,
margin: new EdgeInsets.fromLTRB(0, 0, 0, 30),
child: Visibility(
visible: cameraviewVisbility,
child: RawMaterialButton(
onPressed: () {
_videoOffPage();
setState(() => pressAttention = !pressAttention);
},
child: new Icon(IconData(0xe800, fontFamily: '_kFontFamiiiiiii',
),
color: Theme.Colors.bluecolor,
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: pressAttention ? Colors.transparent: Colors.white54,
padding: const EdgeInsets.all(15.0),
),
),
);
}
void _videoOffPage(){
setState(() {
Scaffold(
body: new Center(
child: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
color: Colors.white,
child: Image(image: new AssetImage('assets/img/videologo.png')
),
),
],
),
),
);
});
}
For opening a page on button press try like this
on first page only
void _videoOffPage(){
Navigator.push(context, MaterialPageRoute(builder: (context) => secondPage()));}
class secondPage extends StatefulWidget {
#override
_secondPageState createState() => _secondPageState();
}
class _secondPageState extends State<secondPage> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: new Center(
child: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
color: Colors.white,
child: Image(image: new AssetImage('assets/img/videologo.png')
),
),
],
),
),
);
}
}

Swipe List Item for more options (Flutter)

Somedays ago I decided to choose an Ui for an app from Pinterest to practice building apps with Flutter but I'm stuck with the Slider which shows an "more" and "delete" button on horizontal drag. Picture on the right.
I don't have enough knowledge to use Gestures combined with Animations to create something like this in flutter. Thats why I hope that someone of you can make an example for everyone like me that we can understand how to implement something like this in a ListView.builder.
(Source)
An gif example from the macOS mail App:
I created a package for doing this kind of layout: flutter_slidable (Thanks Rémi Rousselet for the based idea)
With this package it's easier to create contextual actions for a list item. For example if you want to create the kind of animation you described:
You will use this code:
new Slidable(
delegate: new SlidableDrawerDelegate(),
actionExtentRatio: 0.25,
child: new Container(
color: Colors.white,
child: new ListTile(
leading: new CircleAvatar(
backgroundColor: Colors.indigoAccent,
child: new Text('$3'),
foregroundColor: Colors.white,
),
title: new Text('Tile n°$3'),
subtitle: new Text('SlidableDrawerDelegate'),
),
),
actions: <Widget>[
new IconSlideAction(
caption: 'Archive',
color: Colors.blue,
icon: Icons.archive,
onTap: () => _showSnackBar('Archive'),
),
new IconSlideAction(
caption: 'Share',
color: Colors.indigo,
icon: Icons.share,
onTap: () => _showSnackBar('Share'),
),
],
secondaryActions: <Widget>[
new IconSlideAction(
caption: 'More',
color: Colors.black45,
icon: Icons.more_horiz,
onTap: () => _showSnackBar('More'),
),
new IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
onTap: () => _showSnackBar('Delete'),
),
],
);
There's already a widget for this kind of gesture. It's called Dismissible.
You can find it here. https://docs.flutter.io/flutter/widgets/Dismissible-class.html
EDIT
If you need the exact same transtion, you'd probably have to implement if yourself.
I made a basic example. You'd probably want to tweak the animation a bit, but it's working at least.
class Test extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
double rating = 3.5;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new ListView(
children: ListTile
.divideTiles(
context: context,
tiles: new List.generate(42, (index) {
return new SlideMenu(
child: new ListTile(
title: new Container(child: new Text("Drag me")),
),
menuItems: <Widget>[
new Container(
child: new IconButton(
icon: new Icon(Icons.delete),
),
),
new Container(
child: new IconButton(
icon: new Icon(Icons.info),
),
),
],
);
}),
)
.toList(),
),
);
}
}
class SlideMenu extends StatefulWidget {
final Widget child;
final List<Widget> menuItems;
SlideMenu({this.child, this.menuItems});
#override
_SlideMenuState createState() => new _SlideMenuState();
}
class _SlideMenuState extends State<SlideMenu> with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
initState() {
super.initState();
_controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
}
#override
dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final animation = new Tween(
begin: const Offset(0.0, 0.0),
end: const Offset(-0.2, 0.0)
).animate(new CurveTween(curve: Curves.decelerate).animate(_controller));
return new GestureDetector(
onHorizontalDragUpdate: (data) {
// we can access context.size here
setState(() {
_controller.value -= data.primaryDelta / context.size.width;
});
},
onHorizontalDragEnd: (data) {
if (data.primaryVelocity > 2500)
_controller.animateTo(.0); //close menu on fast swipe in the right direction
else if (_controller.value >= .5 || data.primaryVelocity < -2500) // fully open if dragged a lot to left or on fast swipe to left
_controller.animateTo(1.0);
else // close if none of above
_controller.animateTo(.0);
},
child: new Stack(
children: <Widget>[
new SlideTransition(position: animation, child: widget.child),
new Positioned.fill(
child: new LayoutBuilder(
builder: (context, constraint) {
return new AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return new Stack(
children: <Widget>[
new Positioned(
right: .0,
top: .0,
bottom: .0,
width: constraint.maxWidth * animation.value.dx * -1,
child: new Container(
color: Colors.black26,
child: new Row(
children: widget.menuItems.map((child) {
return new Expanded(
child: child,
);
}).toList(),
),
),
),
],
);
},
);
},
),
)
],
),
);
}
}
EDIT
Flutter no longer allows type Animation<FractionalOffset> in SlideTransition animation property. According to this post https://groups.google.com/forum/#!topic/flutter-dev/fmr-C9xK5t4 it should be replaced with AlignmentTween but this also doesn't work. Instead, according to this issue: https://github.com/flutter/flutter/issues/13812 replacing it instead with a raw Tween and directly creating Offset object works instead. Unfortunately, the code is much less clear.
Updated Code with Null Safety: Flutter: 2.x
Firstly you need to add the flutter_slidable package in your project and add below code then Let's enjoy...
Slidable(
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
child: Container(
color: Colors.white,
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.indigoAccent,
child: Text('$3'),
foregroundColor: Colors.white,
),
title: Text('Tile n°$3'),
subtitle: Text('SlidableDrawerDelegate'),
),
),
actions: <Widget>[
IconSlideAction(
caption: 'Archive',
color: Colors.blue,
icon: Icons.archive,
onTap: () => _showSnackBar('Archive'),
),
IconSlideAction(
caption: 'Share',
color: Colors.indigo,
icon: Icons.share,
onTap: () => _showSnackBar('Share'),
),
],
secondaryActions: <Widget>[
IconSlideAction(
caption: 'More',
color: Colors.black45,
icon: Icons.more_horiz,
onTap: () => _showSnackBar('More'),
),
IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
onTap: () => _showSnackBar('Delete'),
),
],
);
I have a task that needs the same swipeable menu actions I tried answeres of Romain Rastel and Rémi Rousselet. but I have complex widget tree. the issue with that slideable solutions is they go on other widgets(to left widgets of listview). I found a batter solution here someone wrote a nice article medium and GitHub sample is here.
I look at a lot of articles and answers, and find #Rémi Rousselet answer the best fitted to use without third party libraries.
Just put some improvements to #Rémi's code to make it usable in modern SDK without errors and null safety.
Also I smooth a little bit movement, to make the speed of buttons appeared the same as finger movement.
And I put some comments into the code:
import 'package:flutter/material.dart';
class SlidebleList extends StatefulWidget {
const SlidebleList({Key? key}) : super(key: key);
#override
State<SlidebleList> createState() => _SlidebleListState();
}
class _SlidebleListState extends State<SlidebleList> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: ListTile.divideTiles(
context: context,
tiles: List.generate(42, (index) {
return SlideMenu(
menuItems: <Widget>[
Container(
color: Colors.black12,
child: IconButton(
icon: const Icon(Icons.more_horiz),
onPressed: () {},
),
),
Container(
color: Colors.red,
child: IconButton(
color: Colors.white,
icon: const Icon(Icons.delete),
onPressed: () {},
),
),
],
child: const ListTile(
title: Text("Just drag me"),
),
);
}),
).toList(),
),
);
}
}
class SlideMenu extends StatefulWidget {
final Widget child;
final List<Widget> menuItems;
const SlideMenu({Key? key,
required this.child, required this.menuItems
}) : super(key: key);
#override
State<SlideMenu> createState() => _SlideMenuState();
}
class _SlideMenuState extends State<SlideMenu> with SingleTickerProviderStateMixin {
late AnimationController _controller;
#override
initState() {
super.initState();
_controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 200));
}
#override
dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
//Here the end field will determine the size of buttons which will appear after sliding
//If you need to appear them at the beginning, you need to change to "+" Offset coordinates (0.2, 0.0)
final animation =
Tween(begin: const Offset(0.0, 0.0),
end: const Offset(-0.2, 0.0))
.animate(CurveTween(curve: Curves.decelerate).animate(_controller));
return GestureDetector(
onHorizontalDragUpdate: (data) {
// we can access context.size here
setState(() {
//Here we set value of Animation controller depending on our finger move in horizontal axis
//If you want to slide to the right, change "-" to "+"
_controller.value -= (data.primaryDelta! / (context.size!.width*0.2));
});
},
onHorizontalDragEnd: (data) {
//To change slide direction, change to data.primaryVelocity! < -1500
if (data.primaryVelocity! > 1500)
_controller.animateTo(.0); //close menu on fast swipe in the right direction
//To change slide direction, change to data.primaryVelocity! > 1500
else if (_controller.value >= .5 || data.primaryVelocity! < -1500)
_controller.animateTo(1.0); // fully open if dragged a lot to left or on fast swipe to left
else // close if none of above
_controller.animateTo(.0);
},
child: LayoutBuilder(builder: (context, constraint) {
return Stack(
children: [
SlideTransition(
position: animation,
child: widget.child,
),
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
//To change slide direction to right, replace the right parameter with left:
return Positioned(
right: .0,
top: .0,
bottom: .0,
width: constraint.maxWidth * animation.value.dx * -1,
child: Row(
children: widget.menuItems.map((child) {
return Expanded(
child: child,
);
}).toList(),
),
);
})
],
);
})
);
}
}
i had the same problem and and as the accepted answer suggests, i used flutter_slidable
but i needed a custom look for the actions and also i wanted them to be vertically aligned not horizontal.
i noticed that actionPane() can take a list of widgets as children not only
SlidableAction.
so i was able to make my custom actions,and wanted to share the code and results with you here.
this is the layout
this is the code i used :
ListView.builder(
itemBuilder: (context, index) {
return Slidable(
startActionPane: ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.25,
// A pane can dismiss the Slidable.
// All actions are defined in the children parameter.
children: [
Expanded(
flex: 1,
child: Card(
margin: const EdgeInsets.symmetric(
horizontal: 8, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
Expanded(
child: InkWell(
child: Container(
width: double.infinity,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Icon(Icons.edit,
color:
Colors.deepPurple),
Text(
LocalizationKeys.edit.tr,
style: TextStyle(
color:
Colors.deepPurple,
fontSize: 16),
),
],
),
),
onTap: () {},
),
),
Container(
height: 1,
color: Colors.deepPurple,
),
Expanded(
child: InkWell(
child: Container(
width: double.infinity,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Icon(Icons.delete,
color: Colors.red),
Text(
LocalizationKeys
.app_delete.tr,
style: TextStyle(
color: Colors.red,
fontSize: 16),
),
],
),
),
onTap: () {},
),
),
],
),
),
),
]),
child: Card(
margin: EdgeInsets.all(16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
elevation: 0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 16),
Text(_lecturesViewModel
.lectures.value[index].centerName),
SizedBox(height: 16),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(_lecturesViewModel
.lectures.value[index].classLevel),
Text(_lecturesViewModel
.lectures.value[index].material),
],
),
SizedBox(height: 16),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.location_pin),
Text(_lecturesViewModel
.lectures.value[index].city),
Text(_lecturesViewModel
.lectures.value[index].area),
],
),
SizedBox(height: 16),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Icon(Icons.calendar_today),
Text(_lecturesViewModel
.lectures.value[index].day),
],
),
Container(
height: 1,
width: 60,
color: Colors.black,
),
Column(
children: [
Icon(Icons.punch_clock),
Text(_lecturesViewModel
.lectures.value[index].time),
],
),
Container(
height: 1,
width: 60,
color: Colors.black,
),
Column(
children: [
Icon(Icons.money),
Text(
"${_lecturesViewModel.lectures.value[index].price.toString()}ج "),
],
)
]),
SizedBox(height: 16),
]),
),
);
},
itemCount: _lecturesViewModel.lectures.length,
physics: BouncingScrollPhysics(),
)

Categories

Resources