Here's how you can implement the toggle design you wanted :
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class AnimatedToggle extends StatefulWidget {
final List<String> values;
final ValueChanged onToggleCallback;
final Color backgroundColor;
final Color buttonColor;
final Color textColor;
AnimatedToggle({
@required this.values,
@required this.onToggleCallback,
this.backgroundColor = const Color(0xFFe7e7e8),
this.buttonColor = const Color(0xFFFFFFFF),
this.textColor = const Color(0xFF000000),
});
@override
_AnimatedToggleState createState() => _AnimatedToggleState();
}
class _AnimatedToggleState extends State<AnimatedToggle> {
bool initialPosition = true;
@override
Widget build(BuildContext context) {
return Container(
width: Get.width * 0.6,
height: Get.width * 0.13,
margin: EdgeInsets.all(20),
child: Stack(
children: <Widget>[
GestureDetector(
onTap: () {
initialPosition = !initialPosition;
var index = 0;
if (!initialPosition) {
index = 1;
}
widget.onToggleCallback(index);
setState(() {});
},
child: Container(
width: Get.width * 0.6,
height: Get.width * 0.13,
decoration: ShapeDecoration(
color: widget.backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(Get.width * 0.1),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
widget.values.length,
(index) => Padding(
padding: EdgeInsets.symmetric(horizontal: Get.width * 0.05),
child: Text(
widget.values[index],
style: TextStyle(
fontFamily: 'Rubik',
fontSize: Get.width * 0.045,
fontWeight: FontWeight.bold,
color: const Color(0xAA000000),
),
),
),
),
),
),
),
AnimatedAlign(
duration: const Duration(milliseconds: 250),
curve: Curves.decelerate,
alignment:
initialPosition ? Alignment.centerLeft : Alignment.centerRight,
child: Container(
width: Get.width * 0.33,
height: Get.width * 0.13,
decoration: ShapeDecoration(
color: widget.buttonColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(Get.width * 0.1),
),
),
child: Text(
initialPosition ? widget.values[0] : widget.values[1],
style: TextStyle(
fontFamily: 'Rubik',
fontSize: Get.width * 0.045,
color: widget.textColor,
fontWeight: FontWeight.bold,
),
),
alignment: Alignment.center,
),
),
],
),
);
}
}
Using our `AnimatedToggle` :
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:playground/animated_toggle.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Toggle Button'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _toggleValue = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Toggle Button'),
elevation: 10,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedToggle(
values: ['English', 'Arabic'],
onToggleCallback: (value) {
setState(() {
_toggleValue = value;
});
},
buttonColor: const Color(0xFF0A3157),
backgroundColor: const Color(0xFFB5C1CC),
textColor: const Color(0xFFFFFFFF),
),
Text('Toggle Value : $_toggleValue'),
],
),
),
);
}
}
**Result :**
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/Nd5k7.gif
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.
[![enter image description here][1]][1]
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(),
),
),
),
],
);
},
);
},
),
)
],
),
);
}
}
[1]: https://i.stack.imgur.com/0ODys.png
**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.