flutterandroid-studiowidget

Flutter - How to Extract Widget with onPressed setState inside?


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',
      ),
    ),
  ),

Solution

  • 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(),
          ),
        );
      }
    }