skip to Main Content

I want to Extract a Widget with onPressed setState inside but I get the Message "Reference to an enclosing class method cannot be extracted."
Is there a way to do that?

I would like to divide my code into different widgets so that it remains clear. Here is simplified an example of the code:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Calculator(),
    );
  }
}

class Calculator extends StatefulWidget {
  @override
  _CalculatorState createState() => _CalculatorState();
}

class _CalculatorState extends State<Calculator> {
  var myValue = 0;

  void calculate() {
    myValue = 12;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Container(
            child: TextButton(
              onPressed: () {
                setState(() {
                  calculate();
                });
              },
              child: Text(
                'Button 001',
              ),
            ),
          ),
          TextOutput(myValue: myValue),
        ],
      ),
    );
  }
}

class TextOutput extends StatelessWidget {
  const TextOutput({
    Key key,
    @required this.myValue,
  }) : super(key: key);

  final int myValue;

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(
        myValue.toString(),
      ),
    );
  }
}

The part I want to extract into a separate widget:

  Container(
    child: TextButton(
      onPressed: () {
        setState(() {
          calculate();
        });
      },
      child: Text(
        'Button 001',
      ),
    ),
  ),

3

Answers


  1. Setstate is related to the widget you want to refresh its state. If you extract it to another place, then setState refers to the state of the new widget.

    In your case, the setState will only change the state of the container encapsulating your widget which you are trying to extract and its children, it doesn’t migrate upward.

    Unless, you look for the state of the widget you want, using exact type, and then trigger the state there, but this is overkill, a lot harder, requires more code, than what you currently have.

    Login or Signup to reply.
  2. You can use VoidCallback on extract widget to get onPressed event

    class MyContainer extends StatelessWidget {
      final VoidCallback onTap;
      const MyContainer({
        Key? key,
        required this.onTap,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: TextButton(
            onPressed: onTap,
            child: Text(
              'Button 001',
            ),
          ),
        );
      }
    }
    

    And use like

              MyContainer(
                onTap: () {
                  print("tapped");
    
                  setState(() {
                    calculate();
                  });
                },
              ),
    
    Login or Signup to reply.
  3. Flutter offers VoidCallback and Function(x) (where x can be a different type) for callback-style events between child and parent widgets.

    Simply You can pass Function onPressed; via constructor
    Here is your Extracted Container widget:

    class ExtractedContainer extends StatelessWidget {
      final Function onPressed;
      const ExtractedContainer({
        Key key, @required this.onPressed,
      }) : super(key: key);
      @override
      Widget build(BuildContext context) {
        return Container(
          child: TextButton(
            onPressed: () {
              onPressed();
            },
            child: Text(
              'Button 001',
            ),
          ),
        );
      }
    }
    

    And Here How to use it:

      @override
      Widget build(BuildContext context) {
        return Container(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ExtractedContainer(onPressed: calculate,),
              TextOutput(myValue: myValue),
            ],
          ),
        );
      }
    

    Your full code example

    import 'package:flutter/material.dart';
    
    class MyApp2 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Calculator(),
        );
      }
    }
    
    class Calculator extends StatefulWidget {
      @override
      _CalculatorState createState() => _CalculatorState();
    }
    
    class _CalculatorState extends State<Calculator> {
      var myValue = 0;
    
      void calculate() {
        myValue = 12;
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ExtractedContainer(onPressed: calculate,),
              TextOutput(myValue: myValue),
            ],
          ),
        );
      }
    }
    
    class ExtractedContainer extends StatelessWidget {
      final Function onPressed;
      const ExtractedContainer({
        Key key, @required this.onPressed,
      }) : super(key: key);
      @override
      Widget build(BuildContext context) {
        return Container(
          child: TextButton(
            onPressed: () {
              onPressed();
            },
            child: Text(
              'Button 001',
            ),
          ),
        );
      }
    }
    
    class TextOutput extends StatelessWidget {
      const TextOutput({
        Key key,
        @required this.myValue,
      }) : super(key: key);
    
      final int myValue;
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text(
            myValue.toString(),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search