1

I've build a simple Splash screen. I'm using this package. Now, I'm trying to add version checker via Firebase Remote Config to my Splash Screen. I've written simple version checker code:

Future<bool> versionCheck(context) async {
  //Get Current installed version of app
  final PackageInfo info = await PackageInfo.fromPlatform();
  double currentVersion = double.parse(info.version.trim().replaceAll(".", ""));

  //Get Latest version info from firebase config
  final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance;

  // Using default duration to force fetching from remote server.
  try {
    await _remoteConfig.setConfigSettings(RemoteConfigSettings(
      // cache refresh time
      fetchTimeout: const Duration(seconds: 1),
      // a fetch will wait up to 3 seconds before timing out
      minimumFetchInterval: const Duration(seconds: 3),
    ));
    await _remoteConfig.fetchAndActivate();
    _remoteConfig.getString('force_update_current_version');
    double newVersion = double.parse(_remoteConfig
        .getString('force_update_current_version')
        .trim()
        .replaceAll(".", ""));
    if (newVersion > currentVersion) {
      return false;
    } else {
      return true;
    }
  } catch (exception) {
    return false;
  }
}

I've tested it on my another screen. It works fine.

When I try to add this control to my Splash screen, it says type 'Null' is not a subtype of type 'Widget'

I'm getting my show boolean from SharedPreferences via my checkOnBoarding() method and works well, but when I try to add version control, I fail.

Here it is my Splash Screen code:

class SplashScreen extends StatefulWidget
{
  @override
  _SplashScreenState createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  bool show = true;
  bool versionOK = false;

  @override
  void initState() {
    checkVersion();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        return Future.value(false);
      },
      child: AnimatedSplashScreen(
        backgroundColor: Colors.white,
        splash: "images/my-logo.png",
        nextScreen: FutureBuilder(
          future: checkOnBoarding(),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              if(snapshot.data == true){
                if(versionOK){
                  return MainScreen();
                } else {
                  return showVersionDialog(context);
                }
              }
              else{
                return IntroScreen();
              }
            } else {
              return CircularProgressIndicator();
            }
          },
        ),
        splashTransition: SplashTransition.sizeTransition,
        duration: 3000,
      ),
    );
  }

  Future<bool> checkVersion() async{
    versionOK = await versionCheck(context);
    return versionOK;
  }

  Future<bool> checkOnBoarding() async{
    final prefs = await SharedPreferences.getInstance();
    show = prefs.getBool('ON_BOARDING') ?? false;
    return show;
  }

  showVersionDialog(context) {
    Future.delayed(Duration.zero,(){
      showDialog<String>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          String title = "New Update Available";
          String message =
              "There is a newer version of app available please update it now.";
          String btnLabel = "OK";
          return Platform.isIOS
              ? new CupertinoAlertDialog(
            title: Text(title),
            content: Text(message),
            actions: <Widget>[
              ElevatedButton(
                child: Text(btnLabel),
                onPressed: () {
                  exit(0);
                },
              ),
            ],
          )
              : new AlertDialog(
            title: Text(title),
            content: Text(message),
            actions: <Widget>[
              ElevatedButton(
                child: Text(btnLabel),
                onPressed: () {
                  SystemNavigator.pop();
                },
              ),
            ],
          );
        },
      );
    });

  }
}

So as you see, after I get my show boolean from SharedPreference, I want to check versionOK and if it false, I want to show a non-dissmisable dialog with one button (via my showVersionDialog method). If it comes true, I want to navigate my Main Screen.

What am I missing?

Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
Wicaledon
  • 710
  • 1
  • 11
  • 26

3 Answers3

1

The problem is this line:

return showVersionDialog(context); 

A builder wants to return a widget, but your showVersionDialog method just opens a Dialog and doesn't return a widget.

I would remove this if:

if(snapshot.data == true){
                if(versionOK){
                  return MainScreen();
                } else {
                  return showVersionDialog(context);
                }
              }
              else{
                return IntroScreen();
              }

and replace it with this:

return snapshot.data ? MainScreen() : IntroScreen();

Instead in your initState you can check for the version and if it's not okay you can show your dialog:

 @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((_) async {
       versionOK = await checkVersion();
      if (!versionOK) {
        await showVersionDialog(context);
      }
    });
    super.initState();
  }
Ozan Taskiran
  • 2,922
  • 1
  • 13
  • 23
  • so how can I do this without creating a new screen ? I just want to show a pop-up on same screen and I don't want to navigate. can you provide me code solution – Wicaledon Sep 27 '22 at 19:19
  • Do i understand it correctly: checkOnBoarding decides if you go to the main or intro screen and checkVersion just returns if the current version is ok? – Ozan Taskiran Sep 27 '22 at 19:35
  • I edited my answer with a possible solution, you can try it out but im not sure if this is what you want. – Ozan Taskiran Sep 27 '22 at 19:53
  • thank you Ozan for your `return snapshot.data ? MainScreen() : IntroScreen();` trick. and for answer, this is so close to my request because we can see the popup before Splash with this answer, but still code is navigating to my Main Screen, anyway I'll give upvote for being close answer :) – Wicaledon Sep 27 '22 at 20:25
  • 1
    Well you can place it inside the MainScreen initState, so it does show up in the MainScreen and not in the SplashScreen :D – Ozan Taskiran Sep 27 '22 at 20:38
1

The issue is showVersionDialog doesn't return a widget.

  showVersionDialog(context) {
    _dialog(context);
    return Scaffold();
  }

  _dialog(context) {
    Future.delayed(Duration.zero, () {
      showDialog<String>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          String title = "New Update Available";
          String message =
              "There is a newer version of app available please update it now.";
          String btnLabel = "OK";
          return AlertDialog(
            title: Text(title),
            content: Text(message),
            actions: <Widget>[
              ElevatedButton(
                child: Text(btnLabel),
                onPressed: () {},
              ),
            ],
          );
        },
      );
    });
  }

Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
0

This error occurs when you need to return a widget but instead the program receives nothing. Unless I'm mistaken, I think the error comes from the showAlertDialog. Try adding return before the showAlertDialog's body.

showVersionDialog(context) {
  // I'm not sure you need the Future.delayed. I would directly 
  // return the show dialog
    **return** showDialog<String>(
        context: context,
         .........

Hope this helps

Tyler2P
  • 2,324
  • 26
  • 22
  • 31
Thalosse
  • 41
  • 3