iamnull90
iamnull90

Reputation: 79

Flutter stateful widget with child not updating state

I'm working on a part of an app that will essentially just be keeping track of physical tokens that are like forms of currency. I'm trying to build a reusable Widget that will take in the state of that token quantity as a parameter, and increment/decrement that based on user interaction. For the sake of clarity, I've just included the decrement part of the Widget. My question: is the state of the token that is getting passed into the widget not updating because it's just a reference to that state? Or am I missing something else.

    class RedeemTokensState extends State<RedeemTokens> {
      int oneQuantity = 0;
      int fiveQuantity = 0;
      int tenQuantity = 0;
      int total = 0;

    Widget _counterWidget(int tokenQuantity) {
        return Row(
          children: <Widget>[
            Expanded(
              child: IconButton(
                icon: Icon(Icons.remove),
                onPressed: () {
                  setState(() {
                    tokenQuantity = tokenQuantity - 1;
                    print(tokenQuantity);
                  });
                },
              ),
            ),
    ),
    }

     Widget _buildOneField() {
    return ListTile(
      title: Text('\$1 Token'),
      trailing: Container(width: 200.0, child: _counterWidget(oneQuantity)),
    );
  }

  Widget _buildFiveField() {
    return ListTile(
      title: Text('\$5 Token'),
      trailing: Container(width: 200.0, child: _counterWidget(fiveQuantity)),
    );
  }

  Widget _buildTenField() {
    return ListTile(
      title: Text('\$10 Token'),
      trailing: Container(width: 200.0, child: _counterWidget(tenQuantity)),
    );
  }

    } 


    // main scaffold with build method
     ... Card(
              child: Container(
                padding: EdgeInsets.all(10.0),
                child: Column(
                  children: <Widget>[
                    _buildOneField(),
                    Divider(),
                    _buildFiveField(),
                    Divider(),
                    _buildTenField(),
                    Divider(),
                    _buildFreshConnectField(),
                  ],
                ),
              ),
            ), 

Upvotes: 2

Views: 3077

Answers (1)

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 658087

A generic solution could look like:

Parent widget

class RedeemTokens extends StatefulWidget {
  @override
  RedeemTokensState createState() => RedeemTokensState();
}

class RedeemTokensState extends State<RedeemTokens> {
  final _quantities = new Map<TokenType, int>.fromIterable(TokenType.values,
      key: (k) => k, value: (k) => 0);

  Widget build(BuildContext context) {
    final widgets = <Widget>[];
    for (final type in _quantities.keys) {
      widgets
        ..add(
          new TokenQuantity(
              tokenType: type,
              quantity: _quantities[type],
              onQuantityUpdated: (newValue) {
                setState(() {
                  print('\$${type.value}: $newValue');
                  print(_quantities);
                  _quantities[type] = newValue;
                });
              }),
        )
        ..add(Divider());
    }

    // widgets.add(_buildFreshConnectField());
    return Card(
      child: Container(
        padding: EdgeInsets.all(10.0),
        child: Column(
          children: widgets,
        ),
      ),
    );
  }
}

Child widget added once per TokenType

class TokenQuantity extends StatelessWidget {
  const TokenQuantity(
      {@required this.tokenType,
      @required this.quantity,
      this.onQuantityUpdated})
      : assert(quantity != null);

  final TokenType tokenType;
  final int quantity;
  final TokenQuantityUpdatedFn onQuantityUpdated;

  Widget _counterWidget() {
    return Row(
      children: <Widget>[
        Text('$quantity'),
        Expanded(
          child: IconButton(
            icon: Icon(Icons.remove),
            onPressed: () {
              if (onQuantityUpdated != null) {
                onQuantityUpdated(quantity - 1);
              }
            },
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text('\$${tokenType.value} Token'),
      trailing: Container(width: 200.0, child: _counterWidget()),
    );
  }
}

Typedef for the event callback

typedef TokenQuantityUpdatedFn = void Function(int newValue);

"Old-style" enum to be able to set custom values.

class TokenType {
  static const one = const TokenType(1);
  static const fife = const TokenType(5);
  static const ten = const TokenType(10);

  static const values = const <TokenType>[one, fife, ten];

  final int value;
  const TokenType(this.value);

  @override
  String toString() => 'TokenType $\$value';
}

Upvotes: 2

Related Questions