How to Create Custom Switch Button in Flutter

Walnut Software
4 min readMar 30, 2024

--

Flutter’s materials include a switch button; however, sometimes this button may not be sufficient for your needs. Therefore, in this article, I will show you how to create your own switch button and how to use it.

Creating a Switch Button

●Step 1 Setting Up the Project:

Open a new terminal. Navigate to the directory where you want to create a new Flutter project using terminal commands. Then, use the following command to create a new Flutter project:

flutter create example_project

You can replace “example_project” with the name of the project you want to create. In my case project name is flutter_switch_button.

● Step 2: Add the code below to the main.dart file (located in the lib folder).

import 'package:flutter/material.dart';
import 'package:flutter_switch_button/custom_switch_button.dart';

void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CustomSwitch(),
);
}
}

● Step 3: Create a custom_switch_button.dart file inside the lib folder and add the following code to it.

import 'package:flutter/material.dart';

class CustomSwitch extends StatefulWidget {
final bool value;
final ValueChanged<bool> onChanged;

CustomSwitch({Key? key, required this.value, required this.onChanged})
: super(key: key);

@override
_CustomSwitchState createState() => _CustomSwitchState();
}

class _CustomSwitchState extends State<CustomSwitch>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<Alignment> _circleAnimation;
bool isFirstCircleVisible = false;

@override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 300));
_circleAnimation = AlignmentTween(
begin: widget.value ? Alignment.centerRight : Alignment.centerLeft,
end: widget.value ? Alignment.centerLeft : Alignment.centerRight,
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
));
}

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
if (_animationController.isCompleted) {
_animationController.reverse();
} else {
_animationController.forward();
}
widget.onChanged(!widget.value);
setState(() {
isFirstCircleVisible = !isFirstCircleVisible;
});
},
child: Container(
width: 80.0,
height: 46.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.white,
),
child: Stack(
children: [
Align(
alignment: _circleAnimation.value,
child: Container(
alignment: widget.value
? ((Directionality.of(context) == TextDirection.rtl)
? Alignment.centerLeft
: Alignment.centerRight)
: ((Directionality.of(context) == TextDirection.rtl)
? Alignment.centerRight
: Alignment.centerLeft),
child: Container(
width: 40.0,
height: 40.0,
decoration: BoxDecoration(
color: widget.value ? Colors.green:Colors.pink,
shape: BoxShape.circle,
),
)
),

),
],
),
),
);
}

@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}

Code Explanation

  1. CustomSwitch Class: Define a new StatefulWidget called CustomSwitch. It takes two required parameters, value (a boolean indicating the current state of the switch) and onChanged (a callback function that will be called when the switch state changes).
  2. _CustomSwitchState Class: Create the state class _CustomSwitchState for the CustomSwitch widget.
  3. Animation Setup: Initialize an AnimationController and an Animation to control the animation of the switch. The animation is an alignment animation that controls the position of a circle within the switch. isFirstCircleVisible is a boolean variable to track the visibility of the first circle.
  4. InitState Method: Override the initState method to initialize the animation controller and set up the animation with appropriate values based on the initial state of the switch.
  5. Build Method: Override the build method to create the widget tree. It returns a GestureDetector that responds to taps. When tapped, it triggers the animation controller to either forward or reverse the animation, calls the onChanged callback, and updates the visibility of the first circle.
  6. Container Structure: The main visual representation of the switch is contained within a Container. It has a white background, rounded corners, and a fixed size.
  7. Stack and Align Widgets: Use a Stack widget to overlay two containers representing the switch's on and off states. The position of the circle is controlled by the Align widget using the animation values.
  8. Circle Decoration: The circle has a dynamic color based on the visibility of the first circle (isFirstCircleVisible). It is either green or red.
  9. Dispose Method: Override the dispose method to properly dispose of the animation controller when the widget is no longer needed, preventing memory leaks.

Usage

The code example below demonstrates how to use a switch button we created.

import 'package:flutter/material.dart';
import 'package:flutter_switch_button/custom_switch_button.dart';

class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool _enable = false;
@override
Widget build(BuildContext context) {
return Center(
child: CustomSwitch(
value: _enable,
onChanged: (bool val){
setState(() {
_enable = val;
});
},
),
);
}
}

Example

Instead of using the ‘container,’ you can use ‘icon’ as an example.

child: Image.asset(
isFirstCircleVisible
? 'assets/icons/switch_button_green.png'
: 'assets/icons/switch_button_pink.png',
height: 50,
width: 50,
),

Happy Coding!

--

--

Walnut Software
Walnut Software

No responses yet