Setstate for statless widget dropdownButton from a stateful parent

半城伤御伤魂 提交于 2021-01-29 09:53:10

问题


I have three widgets first one is LightBulb(stateless) that have some color properties. The second one is widget named as LightColorSelector(stateless) that has a DropdownMenu with string type items Red, Greenand Blue. And, the third class is Classroom(stateful widget) which is the parent of that two classes. My aim is to set the states from that class. I need to set a list of that three colors inside the Classroom, when someone clicks on one of the LightColorSelector items the LightBulb should switches according to clicked color. However, the color inside the LightBulb returns always null. I think it didn't set. Actually, I might know where is the mistake. I think in the LightColorSelector function there is a onChanged property and I didn't set the value into the func(). I marked below where I suspect the mnistake is occur.

import 'package:flutter/material.dart';

// ignore: must_be_immutable
class LightBulb extends StatelessWidget {
  bool isLit;
  Color color;
  LightBulb(bool isLit, Color color) {
    this.isLit = isLit;
    this.color = color;
    print(color.toString());
  }

  Widget build(BuildContext context) {
    return Container(
      color: isLit ? color : Colors.red,
      padding: EdgeInsets.all(5),
      child: isLit ? Text('ON') : Text('OFF'),
    );
  }
}

class LightButton extends StatelessWidget {
  Function func;
  bool isLightOn;
  LightButton(Function func, bool iSLightOn) {
    this.func = func;
    this.isLightOn = iSLightOn;
  }
  String title() {
    if (isLightOn) return "Turn light off";
    return "Turn light on";
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(vertical: 4, horizontal: 12),
      color: Colors.red,
      child: Container(
        color: Colors.blue,
        child: MaterialButton(
          textColor: Colors.white,
          onPressed: () => func(),
          child: Text(
            title(),
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
    );
  }
}

class Classroom extends StatefulWidget {
  @override
  _ClassroomState createState() => _ClassroomState();
}

class _ClassroomState extends State<Classroom> {
  bool isLightOn = false;
  String title = "Not set yet";
  List<Color> lightColor = [Colors.red, Colors.green, Colors.blue];
  Color color;
  String value;
  selectLightColor() {
    setState(() {
      if (value == 'Red') color = lightColor[0];
      if (value == 'Green') color = lightColor[1];
      if (value == 'Blue')
        color = lightColor[2];
      else
        color = Colors.amber;
    });
  }

  onButtonPressed() {
    setState(() {
      isLightOn = !isLightOn;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: Colors.blue,
        padding: EdgeInsets.all(5),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            LightBulb(isLightOn, color),
            LightButton(onButtonPressed, isLightOn),
            LightColorSelector(selectLightColor),
          ],
        ),
      ),
    );
  }
}

class LightColorSelector extends StatelessWidget {
  String initialVal = 'Red';
  Function func;
  LightColorSelector(Function func) {
    this.func = func;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        padding: EdgeInsets.all(10),
        child: DropdownButton(
          value: initialVal,
          onChanged: (value) => func, // =========== Here the error occurs ==========================
          items: <String>['Red', 'Green', 'Blue']
              .map<DropdownMenuItem<String>>((String value) {
            return DropdownMenuItem<String>(
              value: value,
              child: Text(value),
            );
          }).toList(),
        ));
  }
}

Sample output,


回答1:


You can copy paste run full code below
Step 1: Set initial value in _ClassroomState

class _ClassroomState extends State<Classroom> {
  ..
  Color color = Colors.red;
  String value = 'Red';

Step 2: Callback function selectLightColor need parameter selectedValue and use if else if, you have logic error here

selectLightColor(String selectedValue) {
    setState(() {
      value = selectedValue;
      if (selectedValue == 'Red') {
        color = lightColor[0];
      } else if (selectedValue == 'Green') {
        color = lightColor[1];
      } else if (selectedValue == 'Blue')
        color = lightColor[2];
      else
        color = Colors.amber;
    });
  }

Step 3: LightColorSelector constructor and onChanged need to set initialVal and onChanged need to call func(value);

class LightColorSelector extends StatelessWidget {
  String initialVal;
  Function func;

  LightColorSelector(Function func, String value) {
    this.func = func;
    this.initialVal = value;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        padding: EdgeInsets.all(10),
        child: DropdownButton<String>(
          value: initialVal,
          onChanged: (value) {
            initialVal = value;

            func(value);
          },

working demo

full code

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(body: Classroom()),
    );
  }
}

// ignore: must_be_immutable
class LightBulb extends StatelessWidget {
  bool isLit;
  Color color;
  LightBulb(bool isLit, Color color) {
    this.isLit = isLit;
    this.color = color;
    print(color.toString());
  }

  Widget build(BuildContext context) {
    return Container(
      color: isLit ? color : Colors.red,
      padding: EdgeInsets.all(5),
      child: isLit ? Text('ON') : Text('OFF'),
    );
  }
}

class LightButton extends StatelessWidget {
  Function func;
  bool isLightOn;
  LightButton(Function func, bool iSLightOn) {
    this.func = func;
    this.isLightOn = iSLightOn;
  }
  String title() {
    if (isLightOn) return "Turn light off";
    return "Turn light on";
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(vertical: 4, horizontal: 12),
      color: Colors.red,
      child: Container(
        color: Colors.blue,
        child: MaterialButton(
          textColor: Colors.white,
          onPressed: () => func(),
          child: Text(
            title(),
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
    );
  }
}

class Classroom extends StatefulWidget {
  @override
  _ClassroomState createState() => _ClassroomState();
}

class _ClassroomState extends State<Classroom> {
  bool isLightOn = false;
  String title = "Not set yet";
  List<Color> lightColor = [Colors.red, Colors.green, Colors.blue];
  Color color = Colors.red;
  String value = 'Red';

  selectLightColor(String selectedValue) {
    setState(() {
      value = selectedValue;
      if (selectedValue == 'Red') {
        color = lightColor[0];
      } else if (selectedValue == 'Green') {
        color = lightColor[1];
      } else if (selectedValue == 'Blue')
        color = lightColor[2];
      else
        color = Colors.amber;
    });
  }

  onButtonPressed() {
    setState(() {
      isLightOn = !isLightOn;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        //color: Colors.blue,
        padding: EdgeInsets.all(5),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            LightBulb(isLightOn, color),
            LightButton(onButtonPressed, isLightOn),
            LightColorSelector(selectLightColor, value),
          ],
        ),
      ),
    );
  }
}

class LightColorSelector extends StatelessWidget {
  String initialVal;
  Function func;

  LightColorSelector(Function func, String value) {
    this.func = func;
    this.initialVal = value;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        padding: EdgeInsets.all(10),
        child: DropdownButton<String>(
          value: initialVal,
          onChanged: (value) {
            initialVal = value;

            func(value);
          },
          items: <String>['Red', 'Green', 'Blue']
              .map<DropdownMenuItem<String>>((String value) {
            return DropdownMenuItem<String>(
              value: value,
              child: Text(value),
            );
          }).toList(),
        ));
  }
}



回答2:


StatelessWidget doesn't have a state so you can't set state in stateless widget, this is why it called stateless. If you want to set state you need to use stateful widget



来源:https://stackoverflow.com/questions/65682209/setstate-for-statless-widget-dropdownbutton-from-a-stateful-parent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!