larryaasen / upgrader

A Flutter package for prompting users to upgrade when there is a newer version of the app in the store.
MIT License
531 stars 262 forks source link

`UpgradeCard` disappears / never appears if `upgrader` is changed #424

Open qwertie opened 2 weeks ago

qwertie commented 2 weeks ago

My app uses UpgradeCard to show when there is an upgrade available, but the message I want to show depends on whether the upgrade is mandatory. And whether the upgrade is mandatory depends on async queries to PackageInfo.fromPlatform() and to the backend server. Due to the async requests, there is a race condition between showing the UpgradeCard and choosing the correct message to show.

To deal with this, I decided to force the UpgradeCard to re-render when the message has potentially changed. Apparently this is not actually necessary, as re-rendering the parent component causes the UpgradeCard to re-render and pick up the new message even when none of its properties have changed.

Still, it was confusing that changing the upgrader parameter makes the UpgradeCard disappear (or, in case of two renders in quick succession, prevents it from showing up in the first place) given my code which looked something like this:

class MainScreen extends StatefulWidget {
  const MainScreen({super.key});

  @override State<StatefulWidget> createState() => MainScreenState();
}

class MainScreenState extends State<MainScreen> {
  MainScreenState() {
    PackageInfo.fromPlatform().then((packageInfo) {
      setState(() {
        _upgraderMessages.currentAppVersion = Version.parse(packageInfo.version);
      });
    });
  }

  final UpgraderMessagePicker _upgraderMessages = UpgraderMessagePicker();
  Upgrader? _upgrader;
  String? _upgraderMessageBody;

  @override
  Widget build(BuildContext context) {
    // Pick upgrader message, in case an upgrade is available
    _upgraderMessages.state = Provider.of<_____>(context, listen: true).state;
    if (_upgraderMessageBody != _upgraderMessages.body) {
      _upgraderMessageBody = _upgraderMessages.body;
      _upgrader = Upgrader(messages: _upgraderMessages, debugLogging: true);
    }

    return Scaffold(
        appBar: AppBar(title: const Text("My App")),
        body: ListView(children: [
              UpgradeCard(showIgnore: false, upgrader: _upgrader),
              const OtherStuff(),
            ])
      );
  }
}

class UpgraderMessagePicker extends UpgraderMessages with ChangeNotifier {
  Version currentAppVersion = Version(999, 0, 0);
   ...
}

I believe this happens because UpgradeCardState.initState calls Upgrader.initialize(). If UpgradeCardState.build were to call Upgrader.initialize() instead, the card would not disappear.

I was able to work around the problem by adding _upgrader!.initialize() and the end of the if (_upgraderMessageBody != _upgraderMessages.body) block. Unfortunately this has the side effect of causing a second upgrade check to happen, so in the end I figured out that I should actually send the same instance of Upgrader every time.

Other issues I noticed