Home > Mobile >  How to have non-final fields in Stateful Widget Constructor?
How to have non-final fields in Stateful Widget Constructor?

Time:12-13

So basically I have 2 widgets, Widget 1 which is a stateful widget, and Widget 2 which is also a stateful widget and is a child in Widget 1. In widget 1, when I run setState(){...}, widget 2 is obviously rebuilt however some of the fields aren't updated.

Widget 2 Example:


class WidgetTwo extends StatefulWidget {
  final String noChangeStr;
  final int willChangeInt
  final bool willChangeBool;
  WidgetTwo({
    Key? key,
    required this.noChangeStr,
    required this.willChangeInt,
    required this.willChangeBool,
  }) : super(key: key);

  @override
  State<WidgetTwo> createState() => _WidgetTwoState();
}

class _WidgetTwoState extends State<WidgetTwo> {
  late bool _willChangeBool;
  late int _willChangeInt;

  @override
  void initState() {
    super.initState();
    _willChangeBool = this.widget.willChangeBool;
    _willChangeInt = this.widget.willChangeInt;
  }

  @override
  void dispose() {
    super.dispose();
  }
  ...
}

The reason I have made the fields that WILL change final is because otherwise, flutter warns:

This class (or a class that this class inherits from) is marked as '@immutable', but one or more of its instance fields aren't final

The issue:

When setState is called in Widget 1, the initState in Widget 2 doesn't run again and so therefore _willChangeBool and _willChangeInt aren't updated to the new value. I know that initState only runs once but I thought since widget 2 is supposed to be rebuilt due to widget 1 having used setState, that the initState in Widget 2 would rerun as well.

I have also tried using didChangeDependencies() but that results in the same issue.

Any idea on how to properly implement this? I don't think I should ignore the warning but I can't see any other way to do it. The functionality works as intended if I just set the non-final fields to not final.

Thank you!

CodePudding user response:

Here is how a WidgetOne could update its state and how a WidgetTwo could then respond to it.

import 'package:flutter/material.dart';

void main() {
  runApp(const WidgetOne());
}

class WidgetOne extends StatefulWidget {
  const WidgetOne({Key? key}) : super(key: key);

  @override
  _WidgetOneState createState() => _WidgetOneState();
}

class _WidgetOneState extends State<WidgetOne> {
  String noChangeStr = 'x';
  int willChangeInt = 1;
  bool willChangeBool = true;
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Demo',
      home: Scaffold(
        floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.ac_unit),
          onPressed: () {
            setState(() {
              noChangeStr = 'a';
              willChangeInt = 2;
              willChangeBool = false;
            });
          },
        ),
        body: WidgetTwo(
          noChangeStr: noChangeStr,
          willChangeBool: willChangeBool,
          willChangeInt: willChangeInt,
        ),
      ),
    );
  }
}

class WidgetTwo extends StatefulWidget {
  final String noChangeStr;
  final int willChangeInt;
  final bool willChangeBool;
  const WidgetTwo({
    Key? key,
    required this.noChangeStr,
    required this.willChangeInt,
    required this.willChangeBool,
  }) : super(key: key);

  @override
  State<WidgetTwo> createState() => _WidgetTwoState();
}

class _WidgetTwoState extends State<WidgetTwo> {
  @override
  void initState() {
    super.initState();
  }

  @override
  void didUpdateWidget(covariant WidgetTwo oldWidget) {
    if (oldWidget.noChangeStr != widget.noChangeStr) {
      print('noChangeStr changed!');
      print('you could start an animation here or other fancy stuff.');
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
        child: Text(
      '${widget.noChangeStr}, ${widget.willChangeInt}, ${widget.willChangeBool}',
    ));
  }
}

Does this help?

The fields are not final in WidgetOne: their state is changed with setState in WidgetOne.

The fields in WidgetTwo are final: WidgetTwo is rebuild with the new values handed down from WidgetOne (and they do not change there). If you keep WidgetTwo a stateful widget, you can see the changes in the didUpdateWidget(covariant WidgetTwo oldWidget) method.

  • Related