0

I am new to flutter and am kinda lost on how to set up a time to my splash screen so after this time it goes to the main screen. am using rive for the splash screen

import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
void main() {
 runApp(const MaterialApp(home: SimpleAnimation()));
}
class SimpleAnimation extends StatelessWidget {
 const SimpleAnimation({Key? key, this.loading}) : super(key: key);
 final bool? loading;

 @override
 Widget build(BuildContext context) {
   return const Scaffold(
     body: Center(
       child: RiveAnimation.asset('assets/splash/splash.riv',
         fit: BoxFit.cover)
     ),
   );
 }
}
Nayef
  • 13
  • 4

5 Answers5

2
  @override
void initState() { 
//set timer for splash screen
Timer(const Duration(seconds: 4), () async {
//add your logic here 
 Navigator.pushNamedAndRemoveUntil(
        context, ScreenRoute.mainScreen, (route) => false);
super.initState();
}
Ashutosh singh
  • 820
  • 6
  • 17
2

You can set 3 second time in initstate after navigate to which screen you want

class SplashScreen extends StatefulWidget {
  const SplashScreen({Key? key}) : super(key: key);

  @override
  _SplashScreenState createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    
    // after 3 second it will navigate
    
    Future.delayed(const Duration(seconds: 3)).then((val) {
      // Navigation Here
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      // your code
    );
  }
}
Ravi Limbani
  • 1,092
  • 5
  • 16
2

This SimpleAnimation widget shows after the splash screen. While this is StatelessWidget widget, you can define method inside build method. Change Duration(seconds: 2) based on your need.

class SimpleAnimation extends StatelessWidget {
  const SimpleAnimation({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Future.delayed(const Duration(seconds: 2)).then((value) {
      Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => const NextScreen(),
          ));
    });
    return const Scaffold(
      body: Center(
Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
  • 1
    While this approach is for sure the simplest one, it has one bug - the build method might be called several times (to reproduce it - just rotate the device), and each time a new Future will be created, so there might be a situation when navigation will happen several times. – olexa.le Feb 16 '22 at 16:42
  • `StatelessWidget` doesn't rebuild on orientation changes, I've just recheck it. – Md. Yeasin Sheikh Feb 16 '22 at 16:52
  • 2
    True, my bad. According to documentation, "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.". I thought that orientation change might be a "configuration" case. Though the problem is still there. – olexa.le Feb 16 '22 at 17:17
1

As folks already mentioned the straighforward way would be to add a delay and do navigation after it:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: SplashScreen(),
    );
  }
}

class SplashScreen extends StatefulWidget {
  const SplashScreen({Key? key}) : super(key: key);

  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  @override
  void initState() {
    super.initState();
    Future.delayed(const Duration(seconds: 2), () {
      if (mounted) {
        Navigator.of(context).pushReplacement(
          MaterialPageRoute(
            builder: (context) => const MainScreen(),
          ),
        );
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return const ColoredBox(color: Colors.green);
  }
}

class MainScreen extends StatelessWidget {
  const MainScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const ColoredBox(color: Colors.yellow);
  }
}

Though, with this implementation, you'll have to depend on the animation length. So when you'll update animation - you'll have not to forget to update it inside the splash screen. A more reliable (and complex) solution would be to listen to the animation status and do the navigation after the animation finishes. Like this (warning, change ):

class PlayOneShotAnimation extends StatefulWidget {
  const PlayOneShotAnimation({Key? key}) : super(key: key);

  @override
  _PlayOneShotAnimationState createState() => _PlayOneShotAnimationState();
}

class _PlayOneShotAnimationState extends State<PlayOneShotAnimation> {
  late RiveAnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = OneShotAnimation(
      'bounce',
      autoplay: true,
      onStop: () {
        Navigator.of(context).push(
          MaterialPageRoute<void>(
            builder: (context) => const MainScreen(),
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RiveAnimation.network(
          'https://cdn.rive.app/animations/vehicles.riv',
          animations: const ['idle', 'curves'],
          controllers: [_controller],
        ),
      ),
    );
  }
}

class MainScreen extends StatelessWidget {
  const MainScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const ColoredBox(color: Colors.yellow);
  }
}
olexa.le
  • 1,697
  • 10
  • 14
0

This is my approach for splash screen, the advantage of this approach is to make sure that the splash screen launch only once when the app starting.

First define a static bool in app home class to indicate the app launch.

static bool launch = true;

Then at the home attribute in your MaterialApp widget at app home class, check if (launch) is true use a FutureBuilder to launch the splash screen, if (launch) is false set home to your second screen. With FutureBuilder you can set a timer for your splash screen, when it done your second screen will start (credit to https://stackoverflow.com/a/68699447/11619215).

home: launch? FutureBuilder(
        future: Future.delayed(const Duration(seconds: 3)),
        builder: (ctx, timer) =>
        timer.connectionState == ConnectionState.done
            ? const SecondScreen(title: 'Flutter Demo Home Page')
            : appSplashScreen(),
      ): const SecondScreen(title: 'Flutter Demo Home Page'),

In the Second screen, check if (launch) is true then set it to false. This will make sure that the splash screen will only launch once each time your application start.

if(AppHome.launch) {
      AppHome.launch = false;
    }

Below is the full code with appSplashScreen widget at the bottom:

import 'package:flutter/material.dart';

void main() {
  runApp(const AppHome());
}

class AppHome extends StatelessWidget {
  const AppHome({Key? key}) : super(key: key);

  //static bool to indicate the launching of the app
  static bool launch = true;
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: launch? FutureBuilder(
        future: Future.delayed(const Duration(seconds: 3)),
        builder: (ctx, timer) =>
        timer.connectionState == ConnectionState.done
            ? const SecondScreen(title: 'Flutter Demo Home Page')
            : appSplashScreen(),
      ): const SecondScreen(title: 'Flutter Demo Home Page'),
    );
  }
}

class SecondScreen extends StatefulWidget {
  const SecondScreen({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<SecondScreen> createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {

  @override
  Widget build(BuildContext context) {
    //mack sure your splash screen only launch once at your app starting
    if(AppHome.launch) {
      AppHome.launch = false;
    }
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: const Center(
        child: Text(
          'My Second screen',
        ),
      ),
    );
  }
}

Widget appSplashScreen() {
  return Container(
    decoration: const BoxDecoration(
      ////you can add background image/color to your splash screen
      // image: DecorationImage(
      //   image: AssetImage('assets/background.png'),
      //   fit: BoxFit.cover,
      // ),
      color: Colors.white,
    ),
    child: Center(
      child: SizedBox(
        //get MediaQuery from instance of window to get height and width (no need of context)
        height: MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.height*0.5,
        width: MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width*0.5,
        child: Column(
          children: const [
            ////you can add image to your splash screen
            // Image(
            //   image: AssetImage('assets/splashscreen_image.png'),
            // ),
            FittedBox(
                child: Text(
                  'Loading',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    decoration: TextDecoration.none,
                  ),
                )
            ),
            CircularProgressIndicator(),
          ],
        ),
      ),
    ),
  );
}
MAlhamry
  • 323
  • 1
  • 8