Flutter Switch - onChanged Not Changing
Asked Answered
W

10

15

When using the following Switch widget, the isOn value always returns true and never changes.

The Switch only moves position on a swipe too, a tap won't move it. How to resolve?

bool isInstructionView = false;

Switch(
    value: isInstructionView,
    onChanged: (bool isOn) {
      setState(() {
        isInstructionView = isOn;
        print(isInstructionView);
      });
    },
    activeColor: Colors.blue,
    inactiveTrackColor: Colors.grey,
    inactiveThumbColor: Colors.grey,
)

Update: For extra clarity, onChanged always returns isOn as true. Why would this be?

Wheen answered 7/9, 2018 at 12:34 Comment(2)
setState() always change your value to false define it in StatefulWidget extended class instead of State extended.Ark
@JoshKahane same problem with me, did you find a solution please?Gober
F
11

You just need to make sure to declare the bool for the switch toggle outside Widget build to make it global and acessible for SetState method. No need to initstate and etc.

    class ExampleClass extends StatefulWidget {
     @override
     _ExampleClassState createState() => _ExampleClassState();
    }

    class _ExampleClassState extends State<ExampleClass> {

     bool isInstructionView = false;

     @override
     Widget build(BuildContext context) {

      return Container(
       child:  Switch(
       value: isInstructionView,
       onChanged: (isOn) {

         setState(() {
           isInstructionView = isOn
         });

         print(isInstructionView);

        },
        ...
      ),
     );
    }
   }
Flyer answered 15/5, 2020 at 1:50 Comment(0)
V
8

I added additional code chunks according to @Zulfiqar 's answer. I didn't test this code but I m using similar codes in my project. if you want to save it and use in another class or if you want to show latest state for everytime you load you can save the state in a global variable and call it when you load the class. hope it will help..

class Tab_b extends StatefulWidget {

  @override
  State<StatefulWidget> createState() => new _TabsPageState();
}

class _TabsPageState extends State<Tab_b>{
  bool isInstructionView;
  @override
  void initState() {
    isInstructionView = Global.shared.isInstructionView;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: new Text("add data"),
      ),
      body: new Container(
        child:  Switch(
          value: isInstructionView,
          onChanged: (bool isOn) {
            print(isOn);
            setState(() {
             isInstructionView = isOn;
             Global.shared.isInstructionView = isOn;
             isOn =!isOn;
              print(isInstructionView);
            });
          },
          activeColor: Colors.blue,
          inactiveTrackColor: Colors.grey,
          inactiveThumbColor: Colors.grey,
        ),
      ),
    );
  }
}
class Global{
  static final shared =Global();
  bool isInstructionView = false;
}
Veneering answered 24/12, 2018 at 10:35 Comment(0)
A
4
class Tab_b extends StatefulWidget {

bool isInstructionView = false;
@override
State<StatefulWidget> createState() => new _TabsPageState();
}

class _TabsPageState extends State<Tab_b>{

@override
Widget build(BuildContext context) {

return Scaffold(
    appBar: new AppBar(
    title: new Text("add data"),
),
body: new Container(
  child:  Switch(
    value: widget.isInstructionView,
    onChanged: (bool isOn) {
      print(isOn);
      setState(() {
        widget.isInstructionView = isOn;
        print(widget.isInstructionView);
      });
    },
    activeColor: Colors.blue,
    inactiveTrackColor: Colors.grey,
    inactiveThumbColor: Colors.grey,
  ),
),
);
}
Ark answered 7/9, 2018 at 12:55 Comment(4)
Thanks, but it makes no difference it being declared there. I can swap it there like so: isInstructionView = !isInstructionView;. The issue is isOn doesn't change, its always provided as true.Wheen
It return always true because of when you setState it change isInstructionView value to false and this value is assigned to switch value i.e. it return again it's initial state.Ark
I'm sure I understand, but even with your change, it doesn't affect it because 'isOn' value returned by onChanged never changes.Wheen
@JoshKahane This code works for me I just move bool isInstructionView = false; under my main class then I called it like this value: widget.isInstructionView, then in setState() i called widget.isInstructionView = isOnGober
A
4

Here Is my code for toggle button

class ToggleButtonScreen extends StatefulWidget {
 @override
 _ToggleButtonScreenState createState() => _ToggleButtonScreenState();
}

class _ToggleButtonScreenState extends State<ToggleButtonScreen> {
 bool _value = false;

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: SafeArea(
       child: Center(
         child: Container(
           decoration: BoxDecoration(
             image: DecorationImage(
               image: _value ? AssetImage("images/cnw.png") : AssetImage("images/cnw.png"),
               fit: BoxFit.cover,
             ),
           ),
           child: Padding(
             padding: EdgeInsets.all(AppDimens.EDGE_REGULAR),
             child: Column(
               children: [
                // _customSwitchButton(),
                 _normalToggleButton(),
               ],
             ),
           ),
         ),
       ),
     ),
   );
 }

Widget _normalToggleButton () {
   return Container(
     child: Transform.scale(
       scale: 2.0,
       child: Switch(
         activeColor : Colors.greenAccent,
         inactiveThumbColor: Colors.redAccent,
         value: _value,
           activeThumbImage: AssetImage("images/cnw.png"),
           inactiveThumbImage : AssetImage("images/simple_interest.png"),
           onChanged: (bool value){
            setState(() {
              _value = value;
            });
           },
       ),
     ),
   );
}
}
Aphrodisiac answered 24/11, 2020 at 11:49 Comment(0)
J
4

I faced the same issue , the problem is your

bool isInstructionView = false;

is in same build method which will get rebuild due to change of switch to render new UI on setState()

Solution is to move it out of function Scope to Class Scope so that your variable do not change on rendering Widget again

Jehial answered 12/7, 2022 at 10:23 Comment(0)
B
2

You need to rebuild the widget when the state changes. refer the documentation https://docs.flutter.io/flutter/material/Switch/onChanged.html

Ballata answered 31/3, 2019 at 11:49 Comment(0)
D
1

To get Switch to work , move the setState(() {}) outside of Switch in a callback function .

// Switch  Widget
    Switch( value: _toggleState,
            onChanged: _attemptChange,
            ),
    
//Callback
 void _attemptChange(bool newState) {
    setState(() {
      _toggleState = newState;
      newState ? _switchCase = 'ON' : _switchCase = 'OFF';
    });
Doubletongue answered 3/4, 2021 at 5:55 Comment(0)
H
1

Change SwitchListTile instead of Switch will work.

bool isSwitched = false;

SwitchListTile(
    title: Text("title"),
    controlAffinity: ListTileControlAffinity.leading,
    contentPadding: EdgeInsets.symmetric(),
    value: isSwitched ,
    onChanged: (bool value) {
      setState(() {
        isSwitched = value;
      });
    }
)
Herriot answered 1/7, 2022 at 14:22 Comment(0)
H
1

Use Transform when use Switch will work.

 bool isSwitched = false;

 Transform.scale(
  scale: 1.8,
  child: Switch(
    onChanged: (value) {
      setState(() {
        isSwitched = value;
      });
    },
    value: isSwitched,
  ),
 )
Herriot answered 1/7, 2022 at 14:48 Comment(0)
S
-1

I have also got the same problem while I was implementing CupertinoSwitch. I was able to turn it ON but wasn't able to turn it OFF.

According to this example, I tried to put my Switch inside the widget called 'Semantics' and magically it started working.

Below is the code:

body: Column(
    children: <Widget>[
      SizedBox(height: 50.0,),
      Semantics(
        container: true,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            CupertinoSwitch(
              value: _switchValue,
              onChanged: (bool value){
                setState(() {
                  _switchValue = value;
                  _animating = value;
                });
              },
            ),

            Text(
              "${_switchValue ? "On" : "Off"}",
              style: TextStyle(fontSize: 20.0,
                              fontWeight: FontWeight.bold),
            ),
          ],  
        ),
        ),
        SizedBox(
          height: 50.0,
        ),
        Visibility(
            child: CupertinoActivityIndicator(
            animating: _animating,
            radius: 30.0,
          ),
          visible: _animating,
        ),
    ],

  ),

Hope it helps.

Sweater answered 12/11, 2019 at 4:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.