2023-02-19

StatelessWidget gets rebuilt even though parameters stay the same; notifyListeners() with ChangeNotifier

I've got two StatelessWidgets, one is a child of another. There is also a progress update function which updates the state in the external TransferState. Once updateProgress function is being called the TransferIndicator widget gets rebuilt immediately. On the other hand, its parent (TransfersListTile) build method isn't called.

It works as expected, however I can't really work out what's the mechanism that's being used here. How Flutter decides to rebuild the: _TransferIndicator given that the parameter is a string hash that's not being changed, but only used as a lookup ID to reach the map in TransferState and load the status and progress.

Documentation: https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html says:

"The build method of a stateless widget is typically only called in three situations: the first time the widget is inserted in the tree, when the widget's parent changes its configuration, and when an InheritedWidget it depends on changes."

If: notifyListeners(); function is removed, the widget doesn't get rebuilt.

It seem to be closely related to: ChangeNotifier, but couldn't find the exact info how it works.

In doc here: https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple#changenotifier there is an example involving ChangeNotifier, however doesn't the receiving widget need to be wrapped around: Consumer (https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple#consumer)? In my case there is no Consumer wrapping.

class TransfersListTile extends StatelessWidget {
  TransfersListTile(this.transfer, {Key? key}) : super(key: key);
  final Transfer transfer;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: _TransferIndicator(transfer.hash),
      title: Text(transfer.name!),
    );
  }
}

class _TransferIndicator extends StatelessWidget {
  const _TransferIndicator(this.hash, {Key? key}) : super(key: key);
  final String? hash;

  @override
  Widget build(BuildContext context) {
    final status = context.select((TransferState s) => s.map[hash]?.status) ?? TransferStatus.pending;
    final progress = context.select((TransferState s) => s.map[hash].progress.percentage);

    return CircularProgressIndicator(
      value: status == TransferStatus.completed ? 100 : (progress / 100),
    );
  }
}

function:

class TransferState with ChangeNotifier {
  updateProgress(String hash, TransferProgress progress) {
    map[hash]?.progress = progress;
    notifyListeners();
  }
}

and provider part:

runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) => TransferState(),
        ],
        child: MyApp(),
      )
  );


No comments:

Post a Comment