skip to Main Content

I am using a PageController-like structure to open and close my widgets. The expected behaviour is to go to the AfterGame widget after 49 seconds has elapsed. This does indeed happen. However, when this transition happens, the navigateToNextScreenAfterNSeconds is called again somehow, because 49 seconds after the transition (regardless of which widget is open), we transition once again to the AfterGame widget.

It seems that the navigateToNextScreenAfterNSeconds is somehow called upon opening and upon closing the widget, whilst I only want to call it once, when opening the widget. Am I missing some fundamental logic that is implemented in Flutter widget mechanics? There are no other runtime errors or debug messages showing up.

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

  @override
  State<ActualGame> createState() => _ActualGameState();
}

class _ActualGameState extends State<ActualGame> {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    navigateToNextScreenAfterNSeconds(49);

    return GestureDetector(
      onTap: appState.nextQuestion,
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Scaffold(
          body: Center(
            child: Card(
              elevation: 5,
              shape: ContinuousRectangleBorder(
                  borderRadius: const BorderRadius.all(Radius.circular(12)),
                  side:
                      BorderSide(color: Theme.of(context).colorScheme.outline)),
              child: Padding(
                padding: const EdgeInsets.all(15.0),
                child: Text(
                  appState.currentQuestion,
                  style: const TextStyle(fontSize: 35),
                  textAlign: TextAlign.center,
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  void navigateToNextScreenAfterNSeconds(int seconds) {
    var appState = context.watch<MyAppState>();

    Future.delayed(Duration(seconds: seconds), () {
      appState.updateSelectedIndex(2);
    });
  }
}

Code for PageController:

class PageController extends StatelessWidget {
  const PageController({super.key});

  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    Widget page;

    switch (appState.selectedIndex) {
      case 0:
        page = Menu();
        break;
      case 1:
        page = ActualGame();
        break;
      case 2:
        page = AfterGame();
        break;
      default:
        throw UnimplementedError('Error! This page was not found');
    }

    return LayoutBuilder(builder: (context, constraints) {
      return Scaffold(
        // appBar: AppBar(title: const Text("Appbar")),
        body: page,
      );
    });
  }
}

AppState Code:

class MyAppState extends ChangeNotifier {
  int selectedIndex = 0;
  QuestionController q = QuestionController();
  String currentQuestion = '';

  MyAppState() {
    currentQuestion = q.nextQuestion();
  }

  void nextQuestion() {
    currentQuestion = q.nextQuestion();

    notifyListeners();
  }

  void updateSelectedIndex(int index) {
    selectedIndex = index;

    notifyListeners();
  }
}

2

Answers


  1. Chosen as BEST ANSWER

    @mbakabilal 's anwser was a step in the right direction, but since I am using MyAppState, I can't directly use it as a drop in replacement (otherwise I get an error like this). I needed to get MyAppState outside of the build function, and use null aware acces to be able to use it inside initState, this time using a timer as @jamesdlin suggested. This resulted in the following _ActualGameState class.

    class _ActualGameState extends State<ActualGame> {
      Timer? timer;
      MyAppState? appState;
    
      @override
      void initState() {
        super.initState();
    
        timer = Timer.periodic(
            Duration(seconds: 49), (Timer t) => appState!.updateSelectedIndex(2));
      }
    
      @override
      void dispose() {
        timer?.cancel();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        appState = context.watch<MyAppState>();
    
        return GestureDetector(
          onTap: appState?.nextQuestion,
          child: Padding(
            padding: const EdgeInsets.all(20),
            child: Scaffold(
              body: Center(
                child: Card(
                  elevation: 5,
                  shape: ContinuousRectangleBorder(
                      borderRadius: const BorderRadius.all(Radius.circular(12)),
                      side:
                          BorderSide(color: Theme.of(context).colorScheme.outline)),
                  child: Padding(
                    padding: const EdgeInsets.all(15.0),
                    child: Text(
                      appState!.currentQuestion,
                      style: const TextStyle(fontSize: 35),
                      textAlign: TextAlign.center,
                    ),
                  ),
                ),
              ),
            ),
          ),
        );
      }
    }
    

  2. move the navigateToNextScreenAfterNSeconds(49); to initState, becuase it in in build it gets called each time the widgets rebuilds so it would be called many times.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search