Home > Software design >  Flutter - Multiple Future with error "type 'Null' is not a subtype of type 'Widg
Flutter - Multiple Future with error "type 'Null' is not a subtype of type 'Widg

Time:09-28

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?

CodePudding user response:

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: () {},
              ),
            ],
          );
        },
      );
    });
  }

CodePudding user response:

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();
  }

CodePudding user response:

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

  • Related