animated_toggle_switch 0.8.4 copy "animated_toggle_switch: ^0.8.4" to clipboard
animated_toggle_switch: ^0.8.4 copied to clipboard

Fully customizable, draggable and animated switch with multiple choices and smooth loading animation. It has prebuilt constructors for rolling and size animations.

example/lib/main.dart

import 'dart:math';

import 'package:animated_toggle_switch/animated_toggle_switch.dart';
import 'package:example/crazy_switch.dart';
import 'package:example/load_switch.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedToggleSwitch Demo',
      theme: ThemeData(scaffoldBackgroundColor: Colors.white),
      home: const MyHomePage(title: 'AnimatedToggleSwitch'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int value = 0;
  int? nullableValue;
  bool positive = false;
  bool loading = false;

  @override
  Widget build(BuildContext context) {
    ThemeData theme = Theme.of(context);
    const green = Color(0xFF45CC0D);

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: DefaultTextStyle(
        style: theme.textTheme.titleLarge!,
        textAlign: TextAlign.center,
        child: SingleChildScrollView(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'AnimatedToggleSwitch.dual:',
                ),
              ),
              SizedBox(
                child: AnimatedToggleSwitch<bool>.dual(
                  current: positive,
                  first: false,
                  second: true,
                  spacing: 50.0,
                  style: const ToggleStyle(
                    borderColor: Colors.transparent,
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black26,
                        spreadRadius: 1,
                        blurRadius: 2,
                        offset: Offset(0, 1.5),
                      ),
                    ],
                  ),
                  borderWidth: 5.0,
                  height: 55,
                  onChanged: (b) => setState(() => positive = b),
                  styleBuilder: (b) => ToggleStyle(
                      indicatorColor: b ? Colors.red : Colors.green),
                  iconBuilder: (value) => value
                      ? const Icon(Icons.coronavirus_rounded)
                      : const Icon(Icons.tag_faces_rounded),
                  textBuilder: (value) => value
                      ? const Center(child: Text('Oh no...'))
                      : const Center(child: Text('Nice :)')),
                ),
              ),
              const SizedBox(height: 16.0),
              AnimatedToggleSwitch<bool>.dual(
                current: positive,
                first: false,
                second: true,
                spacing: 50.0,
                style: const ToggleStyle(
                  borderColor: Colors.transparent,
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black26,
                      spreadRadius: 1,
                      blurRadius: 2,
                      offset: Offset(0, 1.5),
                    ),
                  ],
                ),
                borderWidth: 5.0,
                height: 55,
                onChanged: (b) => setState(() => positive = b),
                styleBuilder: (b) => ToggleStyle(
                  backgroundColor: b ? Colors.white : Colors.black,
                  indicatorColor: b ? Colors.blue : Colors.red,
                  borderRadius: const BorderRadius.horizontal(
                      left: Radius.circular(4.0), right: Radius.circular(50.0)),
                  indicatorBorderRadius: BorderRadius.circular(b ? 50.0 : 4.0),
                ),
                iconBuilder: (value) => Icon(
                  value
                      ? Icons.access_time_rounded
                      : Icons.power_settings_new_rounded,
                  size: 32.0,
                  color: value ? Colors.black : Colors.white,
                ),
                textBuilder: (value) => value
                    ? const Center(
                        child:
                            Text('On', style: TextStyle(color: Colors.black)))
                    : const Center(
                        child:
                            Text('Off', style: TextStyle(color: Colors.white))),
              ),
              const SizedBox(height: 16.0),
              DefaultTextStyle.merge(
                style: const TextStyle(
                    color: Colors.white,
                    fontSize: 18.0,
                    fontWeight: FontWeight.bold),
                child: IconTheme.merge(
                  data: const IconThemeData(color: Colors.white),
                  child: AnimatedToggleSwitch<bool>.dual(
                    current: positive,
                    first: false,
                    second: true,
                    spacing: 45.0,
                    animationDuration: const Duration(milliseconds: 600),
                    style: const ToggleStyle(
                      borderColor: Colors.transparent,
                      indicatorColor: Colors.white,
                      backgroundColor: Colors.black,
                    ),
                    customStyleBuilder: (context, local, global) {
                      if (global.position <= 0.0) {
                        return ToggleStyle(backgroundColor: Colors.red[800]);
                      }
                      return ToggleStyle(
                          backgroundGradient: LinearGradient(
                        colors: [green, Colors.red[800]!],
                        stops: [
                          global.position -
                              (1 - 2 * max(0, global.position - 0.5)) * 0.7,
                          global.position +
                              max(0, 2 * (global.position - 0.5)) * 0.7,
                        ],
                      ));
                    },
                    borderWidth: 6.0,
                    height: 60.0,
                    loadingIconBuilder: (context, global) =>
                        CupertinoActivityIndicator(
                            color: Color.lerp(
                                Colors.red[800], green, global.position)),
                    onChanged: (b) => setState(() => positive = b),
                    iconBuilder: (value) => value
                        ? const Icon(Icons.power_outlined,
                            color: green, size: 32.0)
                        : Icon(Icons.power_settings_new_rounded,
                            color: Colors.red[800], size: 32.0),
                    textBuilder: (value) => value
                        ? const Center(child: Text('Active'))
                        : const Center(child: Text('Inactive')),
                  ),
                ),
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'AnimatedToggleSwitch.dual with loading animation:',
                ),
              ),
              DefaultTextStyle.merge(
                style: const TextStyle(color: Colors.white),
                child: IconTheme.merge(
                  data: const IconThemeData(color: Colors.white),
                  child: AnimatedToggleSwitch<bool>.dual(
                    current: positive,
                    first: false,
                    second: true,
                    spacing: 45.0,
                    style: const ToggleStyle(
                      borderColor: Colors.transparent,
                      backgroundColor: Colors.black,
                      borderRadius: BorderRadius.all(Radius.circular(4.0)),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.purple,
                          spreadRadius: 1,
                          blurRadius: 1,
                          offset: Offset(0, 0.5),
                        ),
                      ],
                    ),
                    borderWidth: 10.0,
                    height: 50,
                    loadingIconBuilder: (context, global) =>
                        const CupertinoActivityIndicator(color: Colors.white),
                    onChanged: (b) {
                      setState(() => positive = b);
                      return Future<dynamic>.delayed(
                          const Duration(seconds: 2));
                    },
                    styleBuilder: (b) => ToggleStyle(
                        indicatorColor: b ? Colors.purple : Colors.green),
                    iconBuilder: (value) => value
                        ? const Icon(Icons.coronavirus_rounded)
                        : const Icon(Icons.tag_faces_rounded),
                    textBuilder: (value) => value
                        ? const Center(
                            child: Text('Oh no...',
                                style: TextStyle(color: Colors.white)))
                        : const Center(child: Text('Nice :)')),
                  ),
                ),
              ),
              const SizedBox(height: 16.0),
              AnimatedToggleSwitch<bool>.dual(
                current: positive,
                first: false,
                second: true,
                spacing: 45.0,
                animationDuration: const Duration(milliseconds: 600),
                style: const ToggleStyle(
                  borderColor: Colors.transparent,
                  indicatorColor: Colors.white,
                  backgroundColor: Colors.amber,
                ),
                customStyleBuilder: (context, local, global) => ToggleStyle(
                    backgroundGradient: LinearGradient(
                  colors: const [Colors.green, Colors.red],
                  stops: [
                    global.position -
                        (1 - 2 * max(0, global.position - 0.5)) * 0.5,
                    global.position + max(0, 2 * (global.position - 0.5)) * 0.5,
                  ],
                )),
                borderWidth: 6.0,
                height: 60.0,
                loadingIconBuilder: (context, global) =>
                    CupertinoActivityIndicator(
                        color: Color.lerp(
                            Colors.red, Colors.green, global.position)),
                onChanged: (b) {
                  setState(() => positive = b);
                  return Future<dynamic>.delayed(const Duration(seconds: 2));
                },
                iconBuilder: (value) => value
                    ? const Icon(Icons.power_outlined,
                        color: Colors.green, size: 32.0)
                    : const Icon(Icons.power_settings_new_rounded,
                        color: Colors.red, size: 32.0),
                textBuilder: (value) => Center(
                    child: Text(
                  value ? 'On' : 'Off',
                  style: const TextStyle(
                      color: Colors.white,
                      fontSize: 30.0,
                      fontWeight: FontWeight.w600),
                )),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text.rich(
                  TextSpan(children: [
                    const TextSpan(text: 'Switch inspired by package '),
                    TextSpan(
                        text: 'lite_rolling_switch',
                        style: tappableTextStyle,
                        recognizer: TapGestureRecognizer()
                          ..onTap = () => launchUrl(liteRollingSwitchUrl))
                  ]),
                ),
              ),
              DefaultTextStyle.merge(
                style: const TextStyle(
                    color: Colors.white,
                    fontSize: 18.0,
                    fontWeight: FontWeight.bold),
                child: IconTheme.merge(
                  data: const IconThemeData(color: Colors.white),
                  child: AnimatedToggleSwitch<bool>.dual(
                    current: positive,
                    first: false,
                    second: true,
                    spacing: 45.0,
                    animationCurve: Curves.easeInOut,
                    animationDuration: const Duration(milliseconds: 600),
                    style: const ToggleStyle(
                      borderColor: Colors.transparent,
                      indicatorColor: Colors.white,
                      backgroundColor: Colors.black,
                    ),
                    styleBuilder: (value) => ToggleStyle(
                        backgroundColor: value ? green : Colors.red[800]),
                    borderWidth: 6.0,
                    height: 60.0,
                    loadingIconBuilder: (context, global) =>
                        CupertinoActivityIndicator(
                            color: Color.lerp(
                                Colors.red[800], green, global.position)),
                    onChanged: (b) => setState(() => positive = b),
                    iconBuilder: (value) => value
                        ? const Icon(Icons.lightbulb_outline_rounded,
                            color: green, size: 28.0)
                        : Icon(Icons.power_settings_new_rounded,
                            color: Colors.red[800], size: 30.0),
                    textBuilder: (value) => value
                        ? const Align(
                            alignment: AlignmentDirectional.centerStart,
                            child: Text('Active'))
                        : const Align(
                            alignment: AlignmentDirectional.centerEnd,
                            child: Text('Inactive')),
                  ),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text.rich(
                  TextSpan(children: [
                    const TextSpan(text: 'Switch inspired by package '),
                    TextSpan(
                        text: 'toggle_switch',
                        style: tappableTextStyle,
                        recognizer: TapGestureRecognizer()
                          ..onTap = () => launchUrl(toggleSwitchUrl))
                  ]),
                ),
              ),
              AnimatedToggleSwitch<int>.size(
                current: min(value, 2),
                style: ToggleStyle(
                  backgroundColor: const Color(0xFF919191),
                  indicatorColor: const Color(0xFFEC3345),
                  borderColor: Colors.transparent,
                  borderRadius: BorderRadius.circular(10.0),
                  indicatorBorderRadius: BorderRadius.zero,
                ),
                values: const [0, 1, 2],
                iconOpacity: 1.0,
                selectedIconScale: 1.0,
                indicatorSize: const Size.fromWidth(100),
                iconAnimationType: AnimationType.onHover,
                styleAnimationType: AnimationType.onHover,
                spacing: 2.0,
                customSeparatorBuilder: (context, local, global) {
                  final opacity =
                      ((global.position - local.position).abs() - 0.5)
                          .clamp(0.0, 1.0);
                  return VerticalDivider(
                      indent: 10.0,
                      endIndent: 10.0,
                      color: Colors.white38.withValues(alpha: opacity));
                },
                customIconBuilder: (context, local, global) {
                  final text = const ['not', 'only', 'icons'][local.index];
                  return Center(
                      child: Text(text,
                          style: TextStyle(
                              color: Color.lerp(Colors.black, Colors.white,
                                  local.animationValue))));
                },
                borderWidth: 0.0,
                onChanged: (i) => setState(() => value = i),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text.rich(
                  TextSpan(children: [
                    const TextSpan(text: 'Switch inspired by '),
                    TextSpan(
                        text: 'Crazy Switch',
                        style: tappableTextStyle,
                        recognizer: TapGestureRecognizer()
                          ..onTap = () => launchUrl(crazySwitchUrl))
                  ]),
                ),
              ),
              const Padding(
                padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
                child: CrazySwitch(),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text.rich(
                  TextSpan(children: [
                    const TextSpan(text: 'Switch inspired by '),
                    TextSpan(
                        text: 'load_switch',
                        style: tappableTextStyle,
                        recognizer: TapGestureRecognizer()
                          ..onTap = () => launchUrl(loadSwitchUrl))
                  ]),
                ),
              ),
              const Padding(
                padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
                child: LoadSwitch(),
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'Standard AnimatedToggleSwitch.rolling:',
                ),
              ),
              AnimatedToggleSwitch<int>.rolling(
                current: value,
                indicatorIconScale: sqrt2,
                values: const [0, 1, 2, 3],
                onChanged: (i) {
                  setState(() => value = i);
                  return Future<dynamic>.delayed(const Duration(seconds: 3));
                },
                iconBuilder: rollingIconBuilder,
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'Switch with unselected value:',
                ),
              ),
              AnimatedToggleSwitch<int?>.rolling(
                allowUnlistedValues: true,
                styleAnimationType: AnimationType.onHover,
                current: nullableValue,
                values: const [0, 1, 2, 3],
                onChanged: (i) => setState(() => nullableValue = i),
                iconBuilder: rollingIconBuilder,
                customStyleBuilder: (context, local, global) {
                  final color = local.isValueListed
                      ? null
                      : Theme.of(context).colorScheme.error;
                  return ToggleStyle(borderColor: color, indicatorColor: color);
                },
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'You can make any switch vertical:',
                ),
              ),
              AnimatedToggleSwitch<int>.rolling(
                current: value,
                values: const [0, 1, 2, 3],
                indicatorIconScale: sqrt2,
                style: const ToggleStyle(
                  indicatorColor: Colors.blue,
                  borderColor: Colors.blue,
                  backgroundColor: Colors.white,
                ),
                onChanged: (i) => setState(() => value = i),
                iconBuilder: rollingIconBuilder,
                loading: false,
              ).vertical(),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'Customized AnimatedToggleSwitch.rolling:',
                ),
              ),
              const SizedBox(height: 16.0),
              AnimatedToggleSwitch<int>.rolling(
                active: false,
                current: value,
                values: const [0, 1, 2, 3],
                indicatorIconScale: sqrt2,
                onChanged: (i) {
                  setState(() {
                    value = i;
                    loading = true;
                  });
                  return Future<Object?>.delayed(const Duration(seconds: 3))
                      .then((_) => setState(() => loading = false));
                },
                iconBuilder: rollingIconBuilder,
                style: const ToggleStyle(
                  borderColor: Colors.transparent,
                  indicatorBoxShadow: [
                    BoxShadow(
                      color: Colors.black26,
                      spreadRadius: 1,
                      blurRadius: 2,
                      offset: Offset(0, 1.5),
                    )
                  ],
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black26,
                      spreadRadius: 1,
                      blurRadius: 2,
                      offset: Offset(0, 1.5),
                    )
                  ],
                ),
              ),
              const SizedBox(height: 16.0),
              IconTheme.merge(
                data: const IconThemeData(color: Colors.white),
                child: AnimatedToggleSwitch<int>.rolling(
                  current: value,
                  values: const [0, 1, 2, 3],
                  onChanged: (i) => setState(() => value = i),
                  style: const ToggleStyle(
                    indicatorColor: Colors.white,
                    borderColor: Colors.transparent,
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black26,
                        spreadRadius: 1,
                        blurRadius: 2,
                        offset: Offset(0, 1.5),
                      )
                    ],
                  ),
                  indicatorIconScale: sqrt2,
                  iconBuilder: coloredRollingIconBuilder,
                  borderWidth: 3.0,
                  styleAnimationType: AnimationType.onHover,
                  styleBuilder: (value) => ToggleStyle(
                    backgroundColor: colorBuilder(value),
                    borderRadius: BorderRadius.circular(value * 10.0),
                    indicatorBorderRadius: BorderRadius.circular(value * 10.0),
                  ),
                ),
              ),
              const SizedBox(height: 16.0),
              AnimatedToggleSwitch<int>.rolling(
                current: value,
                allowUnlistedValues: true,
                values: const [0, 1, 2, 3],
                onChanged: (i) => setState(() => value = i),
                iconBuilder: rollingIconBuilder,
                separatorBuilder: (index) => const VerticalDivider(),
                borderWidth: 4.5,
                style: ToggleStyle(
                  indicatorColor: Colors.white,
                  backgroundColor: Colors.amber,
                  borderColor: Colors.transparent,
                  borderRadius: BorderRadius.circular(10.0),
                ),
                styleBuilder: (i) => ToggleStyle(
                  indicatorGradient: LinearGradient(
                      colors: [colorBuilder(i), colorBuilder((i + 1) % 4)]),
                ),
                height: 55,
                spacing: 20.0,
                loading: loading,
              ),
              const SizedBox(height: 16.0),
              AnimatedToggleSwitch<int?>.rolling(
                current: nullableValue,
                allowUnlistedValues: true,
                values: const [0, 1, 2, 3],
                onTap: (info) {
                  if (nullableValue == info.tapped?.value) {
                    setState(() => nullableValue = null);
                  }
                },
                onChanged: (i) => setState(() => nullableValue = i),
                iconBuilder: rollingIconBuilder,
                borderWidth: 4.5,
                style: const ToggleStyle(
                  indicatorColor: Colors.white,
                  backgroundGradient:
                      LinearGradient(colors: [Colors.red, Colors.blue]),
                  borderColor: Colors.transparent,
                ),
                height: 55,
                spacing: 20.0,
                loading: loading,
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'You can make any other switch with CustomAnimatedToggleSwitch:',
                ),
              ),
              CustomAnimatedToggleSwitch<bool>(
                current: positive,
                values: const [false, true],
                spacing: 0.0,
                indicatorSize: const Size.square(30.0),
                animationDuration: const Duration(milliseconds: 200),
                animationCurve: Curves.linear,
                onChanged: (b) => setState(() => positive = b),
                iconBuilder: (context, local, global) {
                  return const SizedBox();
                },
                cursors: const ToggleCursors(
                    defaultCursor: SystemMouseCursors.click),
                onTap: (_) => setState(() => positive = !positive),
                iconsTappable: false,
                wrapperBuilder: (context, global, child) {
                  return Stack(
                    alignment: Alignment.center,
                    children: [
                      Positioned(
                          left: 10.0,
                          right: 10.0,
                          height: 20.0,
                          child: DecoratedBox(
                            decoration: BoxDecoration(
                              color: Color.lerp(Colors.black26,
                                  theme.colorScheme.surface, global.position),
                              borderRadius:
                                  const BorderRadius.all(Radius.circular(50.0)),
                            ),
                          )),
                      child,
                    ],
                  );
                },
                foregroundIndicatorBuilder: (context, global) {
                  return SizedBox.fromSize(
                    size: global.indicatorSize,
                    child: DecoratedBox(
                      decoration: BoxDecoration(
                        color: Color.lerp(
                            Colors.white, theme.primaryColor, global.position),
                        borderRadius:
                            const BorderRadius.all(Radius.circular(50.0)),
                        boxShadow: const [
                          BoxShadow(
                              color: Colors.black38,
                              spreadRadius: 0.05,
                              blurRadius: 1.1,
                              offset: Offset(0.0, 0.8))
                        ],
                      ),
                    ),
                  );
                },
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'AnimatedToggleSwitch.size with some custom settings:',
                ),
              ),
              AnimatedToggleSwitch<int>.size(
                current: value,
                values: const [0, 1, 2, 3],
                iconOpacity: 0.2,
                indicatorSize: const Size.fromWidth(100),
                iconAnimationType: AnimationType.onHover,
                styleAnimationType: AnimationType.onHover,
                iconBuilder: (value) => Icon(
                    value.isEven ? Icons.cancel : Icons.access_time_rounded),
                style: const ToggleStyle(
                  borderColor: Colors.transparent,
                ),
                borderWidth: 0.0,
                styleBuilder: (i) {
                  final color = colorBuilder(i);
                  return ToggleStyle(
                    backgroundColor: color.withValues(alpha: 0.3),
                    indicatorColor: color,
                  );
                },
                onChanged: (i) {
                  setState(() => value = i);
                  return Future<dynamic>.delayed(const Duration(seconds: 3));
                },
              ),
              const SizedBox(height: 16.0),
              AnimatedToggleSwitch<int>.size(
                textDirection: TextDirection.rtl,
                current: value,
                values: const [0, 1, 2, 3],
                iconOpacity: 0.2,
                indicatorSize: const Size.fromWidth(100),
                iconBuilder: iconBuilder,
                borderWidth: 4.0,
                iconAnimationType: AnimationType.onHover,
                style: ToggleStyle(
                  borderColor: Colors.transparent,
                  borderRadius: BorderRadius.circular(10.0),
                  boxShadow: [
                    const BoxShadow(
                      color: Colors.black26,
                      spreadRadius: 1,
                      blurRadius: 2,
                      offset: Offset(0, 1.5),
                    ),
                  ],
                ),
                styleBuilder: (i) =>
                    ToggleStyle(indicatorColor: colorBuilder(i)),
                onChanged: (i) => setState(() => value = i),
              ),
              const SizedBox(height: 16.0),
              AnimatedToggleSwitch<bool>.size(
                current: positive,
                values: const [false, true],
                iconOpacity: 0.2,
                indicatorSize: const Size.fromWidth(100),
                customIconBuilder: (context, local, global) => Text(
                    local.value ? 'RAM' : 'CPU',
                    style: TextStyle(
                        color: Color.lerp(
                            Colors.black, Colors.white, local.animationValue))),
                borderWidth: 4.0,
                iconAnimationType: AnimationType.onHover,
                style: ToggleStyle(
                  indicatorColor: Colors.teal,
                  borderColor: Colors.transparent,
                  borderRadius: BorderRadius.circular(10.0),
                  boxShadow: [
                    const BoxShadow(
                      color: Colors.black26,
                      spreadRadius: 1,
                      blurRadius: 2,
                      offset: Offset(0, 1.5),
                    ),
                  ],
                ),
                selectedIconScale: 1.0,
                onChanged: (b) => setState(() => positive = b),
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'AnimatedToggleSwitch.size with a more custom icon and TextDirection.rtl:',
                ),
              ),
              AnimatedToggleSwitch<int>.size(
                textDirection: TextDirection.rtl,
                current: value,
                values: const [0, 1, 2, 3],
                iconOpacity: 0.2,
                indicatorSize: const Size.fromWidth(100),
                customIconBuilder: (context, local, global) {
                  return Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text('${local.value}'),
                      alternativeIconBuilder(context, local, global),
                    ],
                  );
                },
                style: const ToggleStyle(borderColor: Colors.transparent),
                styleBuilder: (i) => ToggleStyle(
                    indicatorColor:
                        i.isEven == true ? Colors.amber : Colors.red),
                onChanged: (i) => setState(() => value = i),
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'AnimatedToggleSwitch.size with custom rotating animation:',
                ),
              ),
              AnimatedToggleSwitch<int>.size(
                current: value,
                values: const [0, 1, 2, 3],
                iconOpacity: 1.0,
                selectedIconScale: 1.0,
                indicatorSize: const Size.fromWidth(25),
                foregroundIndicatorIconBuilder: (context, global) {
                  double pos = global.position;
                  double transitionValue = pos - pos.floorToDouble();
                  return Transform.rotate(
                      angle: 2.0 * pi * transitionValue,
                      child: Stack(children: [
                        Opacity(
                            opacity: 1 - transitionValue,
                            child: iconBuilder(pos.floor())),
                        Opacity(
                            opacity: transitionValue,
                            child: iconBuilder(pos.ceil()))
                      ]));
                },
                iconBuilder: iconBuilder,
                style: const ToggleStyle(
                  borderColor: Colors.red,
                  borderRadius: BorderRadius.all(Radius.circular(8.0)),
                  indicatorBorderRadius: BorderRadius.zero,
                ),
                styleBuilder: (i) => ToggleStyle(
                    indicatorColor:
                        i.isEven == true ? Colors.green : Colors.tealAccent),
                onChanged: (i) => setState(() => value = i),
              ),
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  'AnimatedToggleSwitch.rollingByHeight with custom indicatorSize and borderRadius:',
                ),
              ),
              AnimatedToggleSwitch<int>.rollingByHeight(
                height: 50.0,
                current: value,
                values: const [0, 1, 2, 3],
                onChanged: (i) => setState(() => value = i),
                iconBuilder: rollingIconBuilder,
                indicatorSize: const Size.fromWidth(2),
              ),
              const SizedBox(height: 16.0),
              AnimatedToggleSwitch<int>.rollingByHeight(
                height: 50.0,
                current: value,
                values: const [0, 1, 2, 3],
                onChanged: (i) => setState(() => value = i),
                iconBuilder: rollingIconBuilder,
                indicatorSize: const Size.square(1.5),
                style: ToggleStyle(borderRadius: BorderRadius.circular(75.0)),
              ),
              SizedBox(height: MediaQuery.of(context).padding.bottom + 16.0),
            ],
          ),
        ), // This trailing comma makes auto-formatting nicer for build methods.
      ),
    );
  }

  Color colorBuilder(int value) => switch (value) {
        0 => Colors.blueAccent,
        1 => Colors.green,
        2 => Colors.orangeAccent,
        _ => Colors.red,
      };

  Widget coloredRollingIconBuilder(int value, bool foreground) {
    final color = foreground ? colorBuilder(value) : null;
    return Icon(
      iconDataByValue(value),
      color: color,
    );
  }

  Widget iconBuilder(int value) {
    return rollingIconBuilder(value, false);
  }

  Widget rollingIconBuilder(int? value, bool foreground) {
    return Icon(iconDataByValue(value));
  }

  IconData iconDataByValue(int? value) => switch (value) {
        0 => Icons.access_time_rounded,
        1 => Icons.check_circle_outline_rounded,
        2 => Icons.power_settings_new_rounded,
        _ => Icons.lightbulb_outline_rounded,
      };

  Widget sizeIconBuilder(BuildContext context,
      AnimatedToggleProperties<int> local, GlobalToggleProperties<int> global) {
    return iconBuilder(local.value);
  }

  Widget alternativeIconBuilder(BuildContext context,
      AnimatedToggleProperties<int> local, GlobalToggleProperties<int> global) {
    IconData data = Icons.access_time_rounded;
    switch (local.value) {
      case 0:
        data = Icons.ac_unit_outlined;
        break;
      case 1:
        data = Icons.account_circle_outlined;
        break;
      case 2:
        data = Icons.assistant_navigation;
        break;
      case 3:
        data = Icons.arrow_drop_down_circle_outlined;
        break;
    }
    return Icon(data);
  }
}

const tappableTextStyle = TextStyle(color: Colors.blue);

final toggleSwitchUrl = Uri.parse('https://pub.dev/packages/toggle_switch');
final liteRollingSwitchUrl =
    Uri.parse('https://pub.dev/packages/lite_rolling_switch');
final crazySwitchUrl =
    Uri.parse('https://github.com/pedromassango/crazy-switch');
final loadSwitchUrl = Uri.parse('https://pub.dev/packages/load_switch');
749
likes
160
points
43.6k
downloads
screenshot

Publisher

verified publishersplashbyte.dev

Weekly Downloads

Fully customizable, draggable and animated switch with multiple choices and smooth loading animation. It has prebuilt constructors for rolling and size animations.

Repository (GitHub)
View/report issues

Topics

#ui #switch #toggle #widget #animation

Documentation

API reference

Funding

Consider supporting this project:

buymeacoffee.com
ko-fi.com

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on animated_toggle_switch