DPLYR-dev / SplashScreenFlutterPackage

A small splashscreen used as intro for flutter applications easily with a lot of customizations ❤️🥳
MIT License
294 stars 123 forks source link

navigateAfterFuture called multiple times #55

Open Akiat opened 3 years ago

Akiat commented 3 years ago

First of all thank you for your great package.

The issue The function assigned to navigateAfterFuture is called multiple times (2 or 3 times in my case).

The code

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  MainSingleton mainSingleton = MainSingleton();

  @override
  Widget build(BuildContext context) {
    return SplashScreen(
        routeName: "/",
        navigateAfterFuture: loadFromFuture(),
        title: new Text(
          APP_NAME,
          style: new TextStyle(fontWeight: FontWeight.bold, fontSize: 30.0, color: Colors.green, fontFamily: "Roboto"),
        ),
        image: Image(image: AssetImage('assets/images/logo.png')),
    );
  }

  Future<Widget> loadFromFuture() async {
    await Firebase.initializeApp();
    // To catch background messages
    FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
    FirebaseMessaging.onMessage.listen(firebaseMessagingForegroundHandler);
    String token = await FirebaseMessaging.instance.getToken();
    print("FirebaseMessaging token: $token");

    // These func only call my API to retreive some data.
    bool authorsLoaded = await loadAuthors();
    bool translatorsLoaded = await loadTranslators();
    bool tagsLoaded = await loadTags();
    bool captionsLoaded = await loadCaptions();

    return buildHomePage(authorsLoaded && translatorsLoaded && tagsLoaded && captionsLoaded);
  }

  Widget buildHomePage(bool everythingLoaded) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: APP_NAME,
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: (everythingLoaded) ? HomePage() : buildRetryPage(),
    );
  }

Widget buildRetryPage() {
    return Scaffold(
      appBar: AppBar(title: Text(APP_NAME)),
      body: Container(
        child: Column(
          children: <Widget>[
            Text('PROBLEM, PLEASE RETRY'),
            Padding(padding: EdgeInsets.only(bottom: 20.0)),
            GFButton(
                text: "RETRY",
                onPressed: () async {
                  Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (BuildContext context) => MyApp()));
                }),
          ],
        ),
      ),
    );
  }

Expected behavior It should call my callback only one time, or I done something wrong.

Thanks for your help 👍

diegodiaz22 commented 3 years ago

I am also experiencing the same

ahmedmgh67 commented 3 years ago

That's weird I can't reproduce it, temporarily you can make a variable that becomes true if the function ran and prevent the function from running again, but I will try to investigate it also Thanks for reporting

Akiat commented 3 years ago

Yes this is what I use to avoid multiple calls 👍

kovacpetar commented 3 years ago

I am also experiencing the same behaviour. This is how it happens for me:

  Future<Widget> loadApplicationWithScreenToNavigate() async {
    printError(info: "Called");
    return Future.value(Home());
  }

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Some title',
      debugShowCheckedModeBanner: false,
      theme: buildThemeData(),
      home: SplashScreen(
        navigateAfterFuture: loadApplicationWithScreenToNavigate(),
        backgroundColor: Colors.white,
    );

And in console I can see sometimes it's being called 2 times, sometimes 3 times.

P.S. Code above is pseudo code just to demonstrate the reproduction of this issue,

kuromukira commented 3 years ago

Probably that the state changes and redraws the widget thus calling your Future again 🤔

MeyerMathieu commented 3 years ago

I agree with @kuromukira , it should be a State issue.

A quick workaround worked for me : Setting a boolean to true as soon as the Future is called, and not calling the Future if the boolean is true :

class _SplashScreenPageState extends State<SplashScreenPage> {

  bool credentialsCalled = false ;

  Future<Widget> _retrieveCredentials() async {
    if (!credentialsCalled) {
      await Requests().getCredentials();
      credentialsCalled = true;
    }
    return SearchPage();
  }

  @override
  Widget build(BuildContext context) {

    return SplashScreen(
      navigateAfterFuture: _retrieveCredentials(),
      // [...]
    );
  }

}