Losing bloc state on orientation change

71 views Asked by At

I am trying to make my Flutter mobile app responsive and adaptive.

I am using this widget to accomplish this:

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

  @override
  Widget build(BuildContext context) {
    return DeviceSpecificLayout(
        mobilePortrait: ExampleMobilePortrait(),
        mobileLandscape: ExampleMobileLandscape(),
        tabletPortrait: ExampleTabletPortrait(),
        tabletLandscape: ExampleTabletLandscape(),
    );
  }
}

The DeviceSpecificLayout widget:

class DeviceSpecificLayout extends StatelessWidget {
  final Widget mobilePortrait;
  final Widget mobileLandscape;
  final Widget tabletPortrait;
  final Widget tabletLandscape;

  const DeviceSpecificLayout({
    super.key,
    required this.mobilePortrait,
    required this.mobileLandscape,
    required this.tabletPortrait,
    required this.tabletLandscape,
  });

  @override
  Widget build(BuildContext context) {
    return ScreenTypeLayout.builder(
      mobile: (context) => OrientationLayoutBuilder(
        portrait: (context) => mobilePortrait,
        landscape: (context) => mobileLandscape,
      ),
      tablet: (context) => OrientationLayoutBuilder(
        portrait: (context) => tabletPortrait,
        landscape: (context) => tabletLandscape,
      ),
    );
  }
}

The issue I am encountering is that whenever the app changes orientation my bloc's state is completely lost because of the build when the app switches the orientation.

This is my main.dart, I am using MultiBlocProvider.

      runApp(
        MultiBlocProvider(
          providers: [
            BlocProvider(create: (_) => ExampleBloc1()),
            BlocProvider(create: (_) => ExampleBloc2()),
          ],
          child: ExamplePage(),
        ),
      );

This is the widget that loses it's state on orientation change:

SafeArea(
                              child: BlocBuilder<ExampleBloc, ExampleState>(
                                builder: (context, state) {
                                  return state.exampleVariable == false
                                      ? Padding(
                                          padding: const EdgeInsets.all(3.0),
                                          child: CountDownAnimatedText(
                                            animation: StepTween(begin: 120, end: 0).animate(animationController),
                                            countdownFontSize: 30.0,
                                            instructionTextFontSize: 25.0,
                                          ),
                                        )
                                      : Container();
                                },
                              ),
                            ),

The above widget is a countdown, which on state orientation loses the countdown state and resets at the beginning.

I am also using the https://pub.dev/packages/responsive_builder package.

Maybe I am approaching the responsiveness and adaptiveness of the app wrong.

I'd love any comments on this.

1

There are 1 answers

1
EdwynZN On

Whenever your UI has a drastic change (like orientation, in mobile orientating the screen equals to change the size of it just like in browsers/desktop apps you can resize your window) it force to call all build methods to change whatever is different, that's mostly your logic with ScreenTypeLayout and OrientationLayoutBuilder but also all objects created inside the build method (like your mobilePortrait or mobileLandscape). If each widget creates a controller when one is disposed and the other created the animation will seems to restart:

 ///Have your animation controller created in one initState or hook and avoid create it in the widgets that will be disposed/changed in the widget tree

BlocConsumer<BlocA, BlocAState>(
  listener: (context, state) {
    if (state.exampleVariable) {
      animationController.forward(from: 0);
    } else {
      animationController.value = 0; ///reset
    }
  },
  builder: (context, state) {
    return state.exampleVariable == false
       ? Padding(
          padding: const EdgeInsets.all(3.0),
          child: CountDownAnimatedText(
          animation: StepTween(begin: 120, end: 0).animate(animationController),
          countdownFontSize: 30.0,
          instructionTextFontSize: 25.0,
        ),
      )
    : Container();
  }
)