-1

I have 3 page (all statefull widgets) :

  • Home page
  • Weather page
  • Setting page

The things is when i'm going from home page to weather page with a "Navigator.pushNamed" and going from the weather page to home page with a "Navigator.pop", the next time i'm trying to go to the weather page from the home page, initState method is called again... How i can manage to make it call only the first time and not been called every time i push into the weather page ?

Here my app.dart code :

import 'package:exomind/src/core/views/home_view.dart';
import 'package:exomind/src/features/weather/presentation/views/weather_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import '../injection_container.dart';
import 'core/styles/colors.dart';
import 'features/settings/presentation/bloc/settings_bloc.dart';
import 'features/settings/presentation/views/settings_view.dart';
import 'features/weather/presentation/bloc/weather_bloc.dart';

/// The Widget that configures your application.
class MyApp extends StatelessWidget {
  const MyApp({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Glue the SettingsController to the MaterialApp.
    //
    // The AnimatedBuilder Widget listens to the SettingsController for changes.
    // Whenever the user updates their settings, the MaterialApp is rebuilt.

    return MultiBlocProvider(
        providers: [
          BlocProvider<WeatherBloc>(
              create: (_) => serviceLocator<WeatherBloc>()),
          BlocProvider<SettingsBloc>(
              create: (_) => serviceLocator<SettingsBloc>()
                ..add(
                  const SettingsLoaded(),
                )),
        ],
        child:
            BlocBuilder<SettingsBloc, SettingsState>(builder: (context, state) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,

            // Providing a restorationScopeId allows the Navigator built by the
            // MaterialApp to restore the navigation stack when a user leaves and
            // returns to the app after it has been killed while running in the
            // background.
            restorationScopeId: 'app',

            // Provide the generated AppLocalizations to the MaterialApp. This
            // allows descendant Widgets to display the correct translations
            // depending on the user's locale.
            localizationsDelegates: const [
              AppLocalizations.delegate,
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
              GlobalCupertinoLocalizations.delegate,
            ],
            supportedLocales: const [
              Locale('en', ''), // English, no country code
            ],

            // Use AppLocalizations to configure the correct application title
            // depending on the user's locale.
            //
            // The appTitle is defined in .arb files found in the localization
            // directory.
            onGenerateTitle: (BuildContext context) =>
                AppLocalizations.of(context)!.appTitle,

            // Define a light and dark color theme. Then, read the user's
            // preferred ThemeMode (light, dark, or system default) from the
            // SettingsController to display the correct theme.
            theme:
                ThemeData(fontFamily: 'Circular', primaryColor: kPrimaryColor),
            darkTheme: ThemeData.dark(),
            themeMode: state.themeMode,

            // Define a function to handle named routes in order to support
            // Flutter web url navigation and deep linking.
            onGenerateRoute: (RouteSettings routeSettings) {
              return MaterialPageRoute<void>(
                settings: routeSettings,
                builder: (BuildContext context) {
                  switch (routeSettings.name) {
                    case SettingsView.routeName:
                      return const SettingsView();
                    case WeatherView.routeName:
                      return const WeatherView();
                    case HomeView.routeName:
                      return const HomeView();
                    default:
                      return const HomeView();
                  }
                },
              );
            },
          );
        }));
  }
}

Here my home_view.dart code :

import 'package:flutter/material.dart';

import '../../features/weather/presentation/views/weather_view.dart';

class HomeView extends StatefulWidget {
  const HomeView({Key? key}) : super(key: key);
  static const routeName = '/home';

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView>
    with SingleTickerProviderStateMixin {
  late AnimationController rotationController;

  @override
  void initState() {
    rotationController =
        AnimationController(duration: const Duration(seconds: 1), vsync: this)
          ..repeat();
    super.initState();
  }

  @override
  void dispose() {
    rotationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final double height = MediaQuery.of(context).size.height;
    final double width = MediaQuery.of(context).size.width;

    return Scaffold(
      body: Stack(
        alignment: Alignment.center,
        children: [
          Positioned(
            top: (height / 2),
            child: RotationTransition(
              turns: Tween(begin: 0.0, end: 1.0).animate(rotationController),
              child: IconButton(
                icon: const Icon(Icons.wb_sunny),
                color: Colors.yellow,
                iconSize: (width * 0.2),
                onPressed: () {
         Navigator.of(context).pushNamed(WeatherView.routeName);
                },
              ),
            ),
          )
        ],
      ),
    );
  }
}

Here my weather_view.dart code :

import 'dart:async';
import 'package:exomind/src/features/weather/presentation/bloc/weather_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:percent_indicator/percent_indicator.dart';

class WeatherView extends StatefulWidget {
  const WeatherView({Key? key}) : super(key: key);
  static const routeName = '/weather';

  @override
  State<WeatherView> createState() => _WeatherViewState();
}

class _WeatherViewState extends State<WeatherView>
    with SingleTickerProviderStateMixin {

  @override
  void initState() {
    print("initcalled")
    super.initState();
  }

  @override
  void dispose() {
    rotationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);

    final double width = MediaQuery.of(context).size.width;
    final double height = MediaQuery.of(context).size.height;

    return Scaffold();
  }
}

Any help and explanation would be appreciate :)

Jeremy Dormevil
  • 430
  • 1
  • 8
  • 22

4 Answers4

0

I can't think of a "clean" way of not executing the initState in _WeatherViewState. Are you trying to avoid the same city added to the WeatherBloc more than once? If so, I'd check for the existence of 'city' in the WeatherBloc before adding.

mmaitlen
  • 824
  • 9
  • 17
  • 1
    No i'm trying to avoid the WeatherView to be rebuild all over again and restart from the beginning the LinearPercentIndicator and the event of being added all over again. – Jeremy Dormevil Apr 19 '22 at 22:35
0

In your onGenerateRoute you call the WeatherView constructor each time:

case WeatherView.routeName:
  return const WeatherView();

This in turn will call initState. What you need to do is create the WeatherView page widget once and use it in the onGenerateRoute:

final _weatherView = const WeatherView();

In your onGenerateRoute:

case WeatherView.routeName:
  return _weatherView;
Roslan Amir
  • 1,141
  • 8
  • 16
  • It doesn't work, initstate is still called everytime i push into the WeatherView... – Jeremy Dormevil Apr 20 '22 at 07:48
  • OK. I am not familiar with using keys, but I think using a GlobalKey may be the solution to your problem. Need further investigation. – Roslan Amir Apr 20 '22 at 09:01
  • I have done some testing and it looks like `initState()` is called before the `build()` every time you switch to it using the Navigator no matter what. I guess there's no way to avoid it being called every time. – Roslan Amir Apr 20 '22 at 10:50
0

As @RoslanAmir said there is no way to prevent initstate of been called each time we push into a statefulwidget. So to prevent my event of being added into my bloc each time we push into the stateful widget i add a bool variable to each state to know if the event should be added or not again. For those who want a precise answer don't hesitate.

Jeremy Dormevil
  • 430
  • 1
  • 8
  • 22
-1

Just add a parameter to the Weather page: a boolean that specifies if the rebuild is true or false. (If true, it will call the initState())

This code works fine.

class WeatherView extends StatefulWidget {
  final bool rebuild;
  static const routeName = '/weather';

  WeatherView({
    Key? key,
    required this.rebuild,
  }) : super(key: key);

  @override
  State<WeatherView> createState() => _WeatherViewState();
}

and the WeatherViewState's initState() will be:

@override
void initState() {
  if (widget.rebuild) {
    print("initcalled");
    super.initState();
  } else {
    print("Not called");
  }
}

So, in your app.dart you should now route to the page by doing

case WeatherView.routeName:
     return const WeatherView(rebuild: true); //Choose if rebuild or not by true and false
Dani3le_
  • 1,257
  • 1
  • 13
  • 22
  • I think your `rebuild` parameter should be declared `final`: `final bool rebuild;` since it is `required` and is never **changed** inside the widget. – Roslan Amir Apr 20 '22 at 09:04
  • This is not what i want, becauz in the end i want initState to be called only once the stateful widget is initiate for the first time. After it shouldn't be called again and the statefull should not be rebuild each time i enter the page. Even if after the first time i could set rebuild to false, i think there is a more appropriate way to do this (or i hope so...) – Jeremy Dormevil Apr 20 '22 at 09:08
  • @RoslanAmir Yup. Forgot to change it after I was done doing some tests. Thanks for pointing it out. – Dani3le_ Apr 20 '22 at 10:09
  • @JeremyDormevil Ok, I may have understood now what you are trying to achieve. Couldn't you use this method associated with a strong routing structure? Otherwise, I'm not sure on how you should handle this situation. – Dani3le_ Apr 20 '22 at 10:12