GithubHelp home page GithubHelp logo

splashbyte / animated_toggle_switch Goto Github PK

View Code? Open in Web Editor NEW
61.0 2.0 15.0 533 KB

Simple and animated switch with multiple choices in Flutter. It's an easy way if you don't want something like a DropdownMenu.

License: BSD 3-Clause "New" or "Revised" License

Kotlin 0.06% Swift 0.18% Objective-C 0.02% Dart 98.42% HTML 0.68% Ruby 0.64%

animated_toggle_switch's Introduction

AnimatedToggleSwitch

pub.dev github likes popularity pub points license codecov

buy me a coffee

If you like this package, please like it on pub.dev and star it on GitHub.

Fully customizable, draggable and animated switch with multiple choices and smooth loading animation. It has prebuilt constructors for rolling and size animations, but it also allows you to create your own switches with CustomAnimatedToggleSwitch.
LTR and RTL are both supported.
Switches without an (initial) selection are also possible.
Most builder arguments of AnimatedToggleSwitch have a standard and a custom version. This ensures that you can get started easily and still customize a lot if necessary. There are several options for styling it.

For a slider with a similar look you can check out action_slider.

Example Usage

usage

Examples

AnimatedToggleSwitch.dual()
animated_toggle_switch_example_dual animated_toggle_switch_example_borderradius_builder animated_toggle_switch_example_gradient

Switch inspired by lite_rolling_switch (made with AnimatedToggleSwitch.dual())
animated_toggle_switch_example_lite_rolling_switch

Switch inspired by toggle_switch (made with AnimatedToggleSwitch.size())
animated_toggle_switch_example_toggle_switch

Switch inspired by crazy-switch (made with CustomAnimatedToggleSwitch())
animated_toggle_switch_example_crazy_switch

Switch inspired by load_switch (made with CustomAnimatedToggleSwitch())
animated_toggle_switch_example_loading_switch

AnimatedToggleSwitch.rolling()
animated_toggle_switch_example_1 animated_toggle_switch_example_2 animated_toggle_switch_example_gradient animated_toggle_switch_example_borderradius_builder_2 animated_toggle_switch_example_rolling_separator

You can build any other switch with CustomAnimatedToggleSwitch()
animated_toggle_switch_example_custom_1

AnimatedToggleSwitch.size()
animated_toggle_switch_example_size animated_toggle_switch_example_size_2

AnimatedToggleSwitch.size() with custom rolling animation
animated_toggle_switch_example_6

AnimatedToggleSwitch.rolling() with custom indicatorSize, borderRadius and indicatorBorderRadius
animated_toggle_switch_example_7 animated_toggle_switch_example_8

Easy Usage

Easy to use and highly customizable.

Simple rolling animation

AnimatedToggleSwitch<int>.rolling(
  current: value,
  values: [0, 1, 2, 3],
  onChanged: (i) => setState(() => value = i),
  iconBuilder: iconBuilder,
  // iconList: [...], you can use iconBuilder, customIconBuilder or iconList
  style: ToggleStyle(...), // optional style settings
  ... // many more parameters available
)

Styling

style, styleBuilder, customStyleBuilder and styleList can be used to style an AnimatedToggleSwitch.
For the general look of the switch, you can use style.
For parameters that should change with the selection, you can use styleBuilder or styleList.
If you need additional parameters, you can use customStyleBuilder.

AnimatedToggleSwitch<int>.rolling(
  ...
  style: ToggleStyle(backgroundColor: Colors.red), // backgroundColor is set independently of the current selection
  styleBuilder: (value) => ToggleStyle(indicatorColor: value.isEven ? Colors.yellow : Colors.green)), // indicatorColor changes and animates its value with the selection
  ...
)

Loading animation

animated_toggle_switch_example_rolling_loading
To use the loading animation, you simply have to return a Future in onChanged or onTap. You can alternatively control the loading manually with the loading parameter.
Hence, to disable the loading animation, loading: false must be set.

AnimatedToggleSwitch<int>.rolling(
  current: value,
  values: [0, 1, 2, 3],
  onChanged: (i) async {
    setState(() => value = i);
    await Future.delayed(Duration(seconds: 3));
  },
  loading: false, // for deactivating loading animation
  iconBuilder: iconBuilder,
  ... // many more parameters available
)

Nullable selection

animated_toggle_switch_example_unlisted_value
To use this feature, you simply have to set allowUnlistedValues to true.

AnimatedToggleSwitch<int?>.rolling(
   allowUnlistedValues: true,
   current: nullableValue, // no selection if nullableValue is not contained in values
   values: const [0, 1, 2, 3],
   onChanged: (i) => setState(() => nullableValue = i),
   iconBuilder: iconBuilder,
   indicatorAppearingBuilder: ..., // appearing animation is fully customizable (optional)
)

Fully customizable toggle switch with CustomAnimatedToggleSwitch

CustomAnimatedToggleSwitch<int>(
  current: value,
  values: [0, 1, 2, 3],
  wrapperBuilder: ..., // the builder for the wrapper around the whole switch
  iconBuilder: ..., // the builder for the icons
  foregroundIndicatorBuilder: ..., // a builder for an indicator in front of the icons
  backgroundIndicatorBuilder: ..., // a builder for an indicator behind the icons
  ... // many more parameters available
)

AnimatedToggleSwitch.size with some settings

animated_toggle_switch_example_size

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: [
      BoxShadow(
        color: Colors.black26,
        spreadRadius: 1,
        blurRadius: 2,
        offset: Offset(0, 1.5),
      ),
    ],
  ),
  styleBuilder: (i) => ToggleStyle(indicatorColor: colorBuilder(i)),
  onChanged: (i) => setState(() => value = i),
)

Self-made rolling animation (with foregroundIndicatorIconBuilder)

animated_toggle_switch_example_6

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(), global.indicatorSize)),
        Opacity(
          opacity: transitionValue,
          child: iconBuilder(pos.ceil(), global.indicatorSize))
        ]));
  },
  iconBuilder: iconBuilder,
  style: ToggleStyle(
    borderColor: Colors.red,
    borderRadius: BorderRadius.circular(8.0),
    indicatorBorderRadius: BorderRadius.zero,
  ),
  styleBuilder: (i) => ToggleStyle(indicatorColor: i.isEven == true ? Colors.green : Colors.tealAccent),
  onChanged: (i) => setState(() => value = i),
)

animated_toggle_switch's People

Contributors

maeddin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

animated_toggle_switch's Issues

Indicator hide DecoratedBox hide while dragging

Problem : Indicator hide DecoratedBox hide while dragging
Description : using the custom toggle switch setting with the following code

class _HomePAgeSliderState extends State<HomePageSlider> {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return CustomAnimatedToggleSwitch<bool>(
      current: tripStart,
      values: [false, true],
      dif: double.maxFinite,
      // left right 3
      padding: const EdgeInsets.symmetric(horizontal: 3.0),
      indicatorSize: const Size.square(42.0),
      animationDuration: const Duration(milliseconds: 200),
      animationCurve: Curves.linear,
      onChanged: (b) => setState(() => tripStart = b),
      //  Container for The Another Side  Not in used
      iconBuilder: (context, local, global) {
        return const SizedBox();
      },
      defaultCursor: SystemMouseCursors.click,
      onTap: () => setState(() => tripStart = !tripStart),
      iconsTappable: false,

      // container for the Switch Button
      foregroundIndicatorBuilder: (context, global) {
        return SizedBox.fromSize(
          size: global.indicatorSize,
          child: DecoratedBox(
              decoration: BoxDecoration(
                color: Colors.white,
                //Color.lerp(Colors.white, Colors.blue, global.position),
                borderRadius: BorderRadius.all(Radius.circular(24.0)),
              ),
              child: AnimatedContainer(
                // top 5 left 5 bottom 5 right 6
                padding: const EdgeInsets.fromLTRB(5.0, 5.0, 6.0, 5.0),
                duration: const Duration(milliseconds: 200),
                child: Icon(
                  tripStart ? Icons.close : Icons.arrow_forward_ios,
                  color: Color.lerp(Colors.blue, Colors.black, global.position),
                ),
              )),
        );
      },

      // container for the slider
      wrapperBuilder: (context, global, child) {
        return Stack(
          alignment: Alignment.center,
          children: [
            Positioned(
                left: 0.0,
                right: 0.0,
                height: 48,
                child: DecoratedBox(
                  decoration: BoxDecoration(
                    gradient: tripStart
                        ? SPAColor.getGradientColor(['FFFFFF', '222222'])
                        : SPAColorSet.PrimaryGradient.gradient,
                    // color: Color.lerp(SPAColor.getColorFromHex('2A7DDE'),
                    //     SPAColor.getColorFromHex('5FCFFF'), global.position),
                    borderRadius: const BorderRadius.all(Radius.circular(24.0)),
                  ),
                )),
            child,
            Container(
              // padding right 56 left 24 top bottom 10
              padding: tripStart
                  ? const EdgeInsets.fromLTRB(24.0, 10.0, 56.0, 10.0)
                  : const EdgeInsets.fromLTRB(56.0, 10.0, 24.0, 10.0),
              child: Text(
                tripStart ? '截車中' : '立即Call車',
                style: TextStyle(
                  color: Colors.white,
                  //Color.lerp(Colors.black, Colors.white, global.position),
                  fontSize: 16.0,
                  fontFamily: 'NotoSansHK',
                  fontWeight: FontWeight.w600,
                ),
              ),
            )
          ],
        );
      },
    );
  

Problem Screen Shot : https://vimeo.com/783189742

Change icon or text color

I using the widget with texts, don't used icon, but how I can change my selected text color? Or check current index selected?

I did not find this possibility in the documentation.

If I just set the color in my AppText component, your prop will change the text color of the selected and unselected index and I don't want that, I just need to change the color of the currently selected index

My source code:


  String currentFilterValue = '';
  List<String> listValues = ['Meus', 'Todos'];

  @override
  void initState() {
    super.initState();
    currentFilterValue = widget.isAllContactsFilter ? 'Todos' : 'Meus';
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedToggleSwitch<String>.size(
      textDirection: TextDirection.rtl,
      current: currentFilterValue,
      values: listValues,
      borderRadius: BorderRadius.circular(6),
      indicatorSize: const Size.fromWidth(35),
      height: 30,
      customIconBuilder: (context, local, global) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            AppText(
              text: local.value,
              fontSize: 10,
              fontWeight: FontWeight.w700,
            ),
          ],
        );
      },
      indicatorColor: Colors.white,
      colorBuilder: (i) {
        return AppColors.mainRed;
      },
      borderColor: Colors.transparent,
      onChanged: (i) {
        setState(() => currentFilterValue = i);
        widget.onChangeCallback(i);
      },
    );
  }

My toggle component:

Captura de tela de 2022-04-27 09-04-39
Captura de tela de 2022-04-27 09-05-23

backgroundColor is displayed inaccurately.

Thank you for your contribution to the open-source community. 😋

The background color of this widget appears lighter than the sample color on the right, even though it's actually using the same color value.

image

Code sample

import 'package:animated_toggle_switch/animated_toggle_switch.dart';
import 'package:flutter/material.dart';

class AnimatedTogglePage extends StatefulWidget {
  const AnimatedTogglePage({super.key});

  @override
  State<AnimatedTogglePage> createState() => _AnimatedTogglePageState();
}

class _AnimatedTogglePageState extends State<AnimatedTogglePage> {
  bool _selected = false;

  final _bgColor = Colors.yellow.withOpacity(0.6);

  final _height = 48.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Animated Toggle')),
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            _buildToggleWidget(),
            Container(
              width: _height * 2,
              height: _height,
              color: _bgColor,
            )
          ],
        ),
      ),
    );
  }

  Widget _buildToggleWidget() {
    return AnimatedToggleSwitch<bool>.dual(
      current: _selected,
      first: false,
      second: true,
      height: _height,
      borderWidth: 0,
      onChanged: (value) {
        setState(() {
          _selected = value;
        });
      },
      styleAnimationType: AnimationType.onHover,
      styleBuilder: (value) => ToggleStyle(
        indicatorColor: Colors.red,
        borderColor: Colors.transparent,
        backgroundColor: _bgColor,
      ),
    );
  }
}

Setstate() called after dispose

Not sure whether this error is caused by something inside the toggle_switch. But if I use the dual toggle_switch and make the widget disappear when clicked I get this error:

dart_sdk.js:5463 Uncaught (in promise) Error: setState() called after dispose(): _CreateSquadWidgetState#84245(lifecycle state: defunct, not mounted) This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree. This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose(). at Object.throw_ [as throw] (dart_sdk.js:5463:11) at unique_widget.dart.js:45706:23 at create_squad_widget._CreateSquadWidgetState.new.setState (unique_widget.dart.js:45712:28) at create_squad_widget._CreateSquadWidgetState.new.setState (firebase_auth_manager.dart.js:16738:13) at create_squad_widget._CreateSquadWidgetState.new.<anonymous> (firebase_auth_manager.dart.js:16774:44) at Generator.next (<anonymous>) at dart_sdk.js:41432:33 at _RootZone.runUnary (dart_sdk.js:41265:59) at _FutureListener.thenAwait.handleValue (dart_sdk.js:36468:29) at handleValueCallback (dart_sdk.js:37039:49) at _Future._propagateToListeners (dart_sdk.js:37077:17) at [_completeWithValue] (dart_sdk.js:36912:23) at async._AsyncCallbackEntry.new.callback (dart_sdk.js:36946:35) at Object._microtaskLoop (dart_sdk.js:41740:13) at _startMicrotaskLoop (dart_sdk.js:41746:13) at dart_sdk.js:37302:9

More background info in this discussion: https://github.com/splashbyte/animated_toggle_switch/discussions/49#discussioncomment-7233161

Ability to initialize with null/unselected value

Currently, if I initialize the widget with a null value in current property, it will throw an error Index out of range, It would be nice if the provided value is not in the values property, it init properly but with no option selected.

Accessibility TalkBack for Android covers other switches, unable to read other tabs or select them.

Hi there,

I've got a animated toggle as seen below. With TalkBack turned on, the user is unable to toggle the switches with the reader covering across all three toggles.

Double tapping on the other tabs only re-reads the "Overview" tab name. Semantics widget does not break up the tabs for reader either. The on changed method within the package

Flutter version: 3.22.1
Animated Toggle Switch: ^0.8.2

Screenshot 2024-06-04 at 13 31 44

Thanks,

Mark

foreground icon and background icon do mismatch by a few pixel

I am trying to create a switch with transparent backgrounds. But I noticed that the icons do not overlap perfectly. You can see 1 or 2 pixel of the background icon shine through.

Solutions:

  • perfect overlap
  • hide background icon when foreground is "above it" (would also allow for different sizes)
  • parameters to "shift" foreground icon

Screenshot 2022-10-13 at 05 18 57

Screenshot 2022-10-13 at 05 17 47

Screenshot 2022-10-13 at 05 17 23

Screenshot 2022-10-13 at 05 16 57

Screenshot 2022-10-13 at 05 16 41

Feature Request: Ability to disable toggle switch

I would love to have the ability to disable the toggle switch. My workaround looks something like this:
indicatorColor: isIndicatorDisabled ? Colors.transparent : theme.colorScheme.primary,

Which works for my use case, but does not truly disable the toggle. I would love to be able to pass in a disabled argument to the constructor to prevent interaction. Additionally, I think it would be nice for current to be nullable, and when current is null the indicator is not shown.

translation example

is it possible to add the translation example to the list ? many thanks

msedge_YjIWhC04ra.mp4

not sure how you added the inner padding between the icons and the switch itself

problems with backgroundGradient and backgroundColor

There are currently two bugs when using backgroundGradient in ToggleStyle:

  • backgroundColor in styleBuilder does not overwrite backgroundGradient in style
  • backgroundGradient in style is ignored when colorBuilder is set without assigning backgroundColor

Sometimes the toggle gets triggered two times

I tried using your sample but I notice that when I press the toggle switch, the print that I have added is printed twice.

              AnimatedToggleSwitch<bool>.dual(
                current: positive,
                first: false,
                second: true,
                dif: 40.0,
//                onChanged: (b) => setState(() => positive = b),
                      onChanged: (b) {
                        print('here');
                        setState(() {
                          positive = b;
                        });
                      },
                colorBuilder: (b) => b ? Colors.red : Colors.green,
                iconBuilder: (b, size, active) => b
                    ? Icon(Icons.coronavirus_rounded)
                    : Icon(Icons.tag_faces_rounded),
                textBuilder: (b, size, active) => b
                    ? Center(child: Text('Oh no...'))
                    : Center(child: Text('Nice :)')),
              ),

[Feature request] Add the possibility to toggle off the switch

Context

With allowUnlistedValues: true, we can now have initial value = null, and it means the switch doesn't have anything selected initially. But it's missing a use case: how to unselect the toggle (without adding an intermediate state of null when we only change from value1 to value 2)?

Use case

I tried to use the AnimatedToggleSwitch with the following use case: clicking on the already selected value switches off the toggle (unselects everything). But I only want to change my state to null when I click on the already selected value.

What I tried

I tried to have the onChanged callback deal with this, but the onChanged callback is only called when the value is not the one we have currently selected. Then I tried with onTap callback, but because it doesn't have the currently selected value available, I cannot add a check for myValue != null. This is what I tried:

onChanged: (changed) { // this doesn't get triggered when the value has not changed
  setState(() => myValue = changed);
},
onTap: () {
  if (myValue != null) { // I cannot check what I have tapped, so this is the best I could do
    setState(() => myValue = null);
  }
}

Result/issue

If I tap on the same value as selected, then it's working correctly (because myValue was selected, and it's now unselected). But if I tap on another value, the onTap is triggered, then the onChanged is triggered, so I get the intermediary state myValue==null that messes up my state.

Summary

To sum up what I want to achieve VS what I get:

flowchart TB
  subgraph What I want
    direction TB
    U("value = null");
    U -->|"Click on 'A'"| A(value = A)
    A -->|"Click on 'A'"|U
    A -->|"Click on 'B'"| B("value = B")
  end
  subgraph What I get
    direction TB
    u("value = null");
    u -->|"Click on 'A'"| a(value = A)
    a -->|"Click on 'A'"| u
    a -->|"Click on 'B'"| b1("value = null\n(I want to get rid of this state)")
    b1 -->|"Then"|b2("value = B")
  end
Loading

Feature request

So my feature request would be:

  • either have the onChanged callback be triggered even when the value has not changed,
  • or have a new callback for "toggling off" the value when allowUnlistedValues == true
  • or even, have a parameter to the callback onTap to get what we value are currently tapping on

Please tell me if this is unclear.

Indicator boxShadow is clipped

In this example i want the indicator to have a boxShadow without the entire toggle having a shadow. Right now this boxShadow is clipped. The plugin should expose an option to prevent clipping.

image

Point releases should not have breaking changes

Flutter and Dart require semantic versioning, as that's what makes the caret syntax in pubspec work.

Point releases, like the recent v0.2.3, should not contain breaking changes, as it could break projects using caret syntax. Instead, that release should be named "0.3.0", to indicate breaking changes.

onchanged property always remains false

hello sir the value in on change property is false Always whether my current value is true or false the value in on change is not changing according to my current state

indicatorSize can't be changed

AnimatedToggleSwitch.dual(
current: logic.state.switchState,
first: false,
second: true,
spacing: 0,
indicatorSize: Size(100, 40.h),)

AnimatedToggleSwitch.dual(
current: logic.state.switchState,
first: false,
second: true,
spacing: 0,
indicatorSize: Size(200, 40.h),)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.