Search code examples
flutterdropdownsetstate

Flutter, Why is the new widget not showing up?


Code attached below. How can I create a new widget with a different color of the Shape, selected from the drop-down, by pressing the grey button(COLOR CHANGE), based on what is selected in the drop-down? the top Container has nothing to do here, just a place holder.

I have tested it without the Widgets MySquare,MyRound & MyRectangle to only change the colors based on the drop-down selection and it worked. But using the other widget classes MySquare,MyRound & MyRectangle does NOT seem to redraw the new widgets.

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

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: MyTrial(strShape: 'ROUND'));
  }
}

class MyTrial extends StatefulWidget{
  final String strShape;

  MyTrial({this.strShape});

  @override
  MyTrialState createState() {
    return new MyTrialState();
  }
}

class MyTrialState extends State<MyTrial> {
  List<String> listShapes;
  String strShapeSelected;

  Widget widgetType;

  @override
  void initState() {
    super.initState();
    listShapes = ['SQUARE', 'ROUND','RECTANGLE'];
    strShapeSelected = widget.strShape;

    widgetType = _myGetShape('ROUND');
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(title: Text('Change\nColor'),
        actions: <Widget>[
          /// button to change color
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 10.0),
            child: GestureDetector(onTap: () {
              _myChangeShapeColor(strShapeSelected);
              setState(() {});
              //print('Gesture $strShapeSelected');
            },
              child: new Container(
                child: new Center(
                    child: new Padding(
                      padding: const EdgeInsets.all(5.0),
                      child: new Text('COLOR\nCHANGE',textAlign: TextAlign.center,),
                    )),
                decoration: new BoxDecoration(
                    color: Colors.blueGrey, shape: BoxShape.circle),
              ),
            ),
          ),
          /// drop down list
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 10.0),
            child: DropdownButton<String>(
                items: listShapes.map((String value) {
                  return new DropdownMenuItem(
                    child: new Text('$value'),
                    value: value,
                  );
                }).toList(),
                value: strShapeSelected,
                onChanged: (String newValue) {
                  setState(() {
                    strShapeSelected = newValue;
                  });
                  widgetType = _myGetShape(strShapeSelected);
                }),
          )
        ],),
      body: Column(
        children: <Widget>[
          /// place holder
          Padding(
            padding: const EdgeInsets.all(10.0),
            child: Container(
              height: 80.0, color: Colors.teal,),
          ),
          /// shape widget
          /// changed by drop-down
          Center(child: widgetType),
        ],
      ),
    );
  }

  Widget _myGetShape(String newValue) {
    Widget newShape;
    switch(newValue){
      case 'SQUARE':
        newShape = MySquare(myColor: Colors.brown,);
        break;
      case 'ROUND':
        newShape = MyRound(myColor: Colors.green,);
        break;
      case 'RECTANGLE':
        newShape = MyRectangle(myColor: Colors.blue,);
        break;
    }
    return newShape;
  }

  void _myChangeShapeColor(String strNewShape) {
    switch(strNewShape){
      case 'SQUARE':
        widgetType = MySquare(myColor: Colors.amber,);
        break;
      case 'ROUND':
        widgetType = MyRound(myColor: Colors.lightGreenAccent,);
        break;
      case 'RECTANGLE':
        widgetType = MyRectangle(myColor: Colors.purple,);
        break;
    }
    setState(() {});
  }
}

/// SQUARE
class MySquare extends StatefulWidget{
  final Color myColor;

  MySquare({this.myColor});

  @override
  MySquareState createState() {
    return new MySquareState();
  }
}

class MySquareState extends State<MySquare> {
  Color newColor;

  @override
  void initState() {
    newColor = widget.myColor;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(child: Center(
        child: Text('SQUARE',style: TextStyle(fontSize: 15.0))),
      color: newColor,
      width: 120.0,height: 120.0,);
  }
}

/// ROUND
class MyRound extends StatefulWidget{
  final Color myColor;

  MyRound({this.myColor});

  @override
  MyRoundState createState() {
    return new MyRoundState();
  }
}

class MyRoundState extends State<MyRound> {
  Color newColor;

  @override
  void initState() {
    newColor = widget.myColor;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(child: Center(
        child: Text('ROUND',style: TextStyle(fontSize: 15.0))),
      decoration: BoxDecoration(color: newColor,
          shape: BoxShape.circle),
      width: 120.0,height: 120.0,);
  }
}

/// RECTANGLE
class MyRectangle extends StatefulWidget{
  final Color myColor;

  MyRectangle({this.myColor});

  @override
  MyRectangleState createState() {
    return new MyRectangleState();
  }
}

class MyRectangleState extends State<MyRectangle> {
  Color newColor;

  @override
  void initState() {
    newColor = widget.myColor;
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Container(child: Center(
        child: Text('RECTANGLE',style: TextStyle(fontSize: 15.0))),
      color: newColor,
      width: 200.0,height: 120.0,);
  }
}

Solution

  • You need to Convert your - MySquare,MyRound & MyRectangle classes in StatelessWidget. Rest of the code remain same & it will work as expected.

    Right now you have it as StatefulWidget - so they were maintaining state once initialize. Hence not changing color.

       /// SQUARE
        class MySquare extends StatelessWidget {
          final Color myColor;
          MySquare({this.myColor});
    
          @override
          Widget build(BuildContext context) {
            return Container(
              child: Center(child: Text('SQUARE', style: TextStyle(fontSize: 15.0))),
              color: myColor,
              width: 120.0,
              height: 120.0,
            );
          }
        }
    

    same for rectangle & Round.

    Update:

    If you want to keep the it as StatefulWidget. Then you need to use - didUpdateWidget method.

     class MySquareState extends State<MySquare> {
          Color newColor;
    
          @override
          void initState() {
            newColor = widget.myColor;
            super.initState();
          }
    
          @override
          void didUpdateWidget(MySquare oldWidget) {
            super.didUpdateWidget(oldWidget);
            print('Called');
            newColor = widget.myColor;
          }
    
          @override
          Widget build(BuildContext context) {
            return Container(child: Center(
                child: Text('SQUARE',style: TextStyle(fontSize: 15.0))),
              color: newColor,
              width: 120.0,height: 120.0,);
          }
        }