0

I want to use GoogleBottomNavigationBar with go_router package for the first time in my Flutter project. But I have a problem. How should I use the package with correct and best practices?

I got the below error, While I was changing a tab on GoogleBottomNavBar:

[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): _GNavState#d598b(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0      State.setState.<anonymous closure> (package:flutter/src/widgets/framew<…>

Here are the pages:

Current Page Home Page
current page home page

My codes at below;

main.dart file:

void main() => runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => RootProvider()),
        ],
        child: const MyApp(),
      ),
    );

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      debugShowCheckedModeBanner: false,
      theme: CustomThemeData().lightTheme,
      darkTheme: CustomThemeData().darkTheme,
      themeMode: Provider.of<RootProvider>(context).isNight ? ThemeMode.dark : ThemeMode.light,
      routerConfig: NavigatorRoutes().router,
    );
  }
}

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

  @override
  State<Root> createState() => _RootState();
}

class _RootState extends State<Root> with SingleTickerProviderStateMixin {
  late final AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
    );
  }

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

  @override
  Widget build(BuildContext context) {
    int selectedIndexOfBottomNavigationBar = Provider.of<RootProvider>(context).selectedIndexOfBottomNavigationBar;
    bool isNight = Provider.of<RootProvider>(context).isNight;

    return Scaffold(
      appBar: AppBar(
        title: InkWell(
          child: const Text("go sample"),
          onTap: () => NavigatorManager.instance.go(
            context,
            route: NavigateRoutes.sample.withParaph,
          ),
        ),
        actions: [
          SizedBox(
            width: 90,
            height: 80,
            child: _changeThemeToggleButton(context, isNight),
          ).onlyPadding(right: PaddingManager.commonHorizontal),
        ],
      ),
      body: Center(
        child: NavigatorRoutes().navPages.elementAt(selectedIndexOfBottomNavigationBar),
      ).symmetricpadding(
        horizontal: PaddingManager.commonHorizontal,
        vertical: PaddingManager.commonVertical,
      ),
      bottomNavigationBar: CustomBottomNavigationBar(
        selectedIndexOfBottomNavigationBar: selectedIndexOfBottomNavigationBar,
      ).onlyPadding(
        left: PaddingManager.horizontalOfBottomNavigationBar,
        right: PaddingManager.horizontalOfBottomNavigationBar,
        bottom: PaddingManager.onlyBottomOfBottomNavigationBar,
        top: PaddingManager.onlyTopOfBottomNavigationBar,
      ),
    );
  }

  InkWell _changeThemeToggleButton(BuildContext context, bool isNight) {
    return InkWell(
      onTap: () {
        Provider.of<RootProvider>(context, listen: false).changeIsNight();
        debugPrint(isNight.toString());
        if (!isNight) {
          _animationController.animateTo(0.5);
        } else {
          _animationController.animateTo(-0.5);
        }
      },
      child: Lottie.asset(
        LottieUrls.pathOfToggleThemeButton,
        fit: BoxFit.cover,
        controller: _animationController,
        onLoaded: (composition) {
          _animationController
            ..duration = composition.duration
            ..value = 0.5;
        },
      ),
    );
  }
}

navigator_routes.dart file:

/// The code defines a class called `NavigatorRoutes` that encapsulates the navigation routes and pages
/// for a Flutter application.
final class NavigatorRoutes {
  /// The code is creating an instance of the `NavigatorRoutes` class and initializing its `_router`
  /// property with a `GoRouter` object. The `GoRouter` object is configured with the following
  /// parameters:
  NavigatorRoutes()
      : _router = GoRouter(
          initialLocation: NavigateRoutes.root.justParaph,
          routes: _items,
          errorBuilder: (context, state) => const Scaffold(
            body: Center(
              child: Text('Page not found'),
            ),
          ),
        );

  final GoRouter _router;

  /// The `static final _items` is a list of `GoRoute` objects that define the navigation routes and
  /// their corresponding builders. Each `GoRoute` object represents a specific route in the application.
  static final _items = [
    GoRoute(
      path: NavigateRoutes.root.justParaph,
      name: NavigateRoutes.root.name,
      builder: (context, state) => const Root(),
      routes: [
        GoRoute(
          path: NavigateRoutes.sample.name,
          builder: (context, state) => const Sample(),
        ),
      ],
    ),
  ];
  static const List<Widget> _navPages = [
    Home(),
    SearchPlaces(),
    CurrentPlace(),
  ];

  GoRouter get router => _router;
  List<Widget> get navPages => _navPages;
}

navigator_manager.dart file:

final class NavigatorManager {
  NavigatorManager._();

  static NavigatorManager instance = NavigatorManager._();

  void go(BuildContext context, {required String route, Object? extras}) {
    // GoRouter.of(context).goNamed(route);
    context.go(route);
  }
}

custom_bottom_navigation_bar.dart file:

/// The `CustomBottomNavigationBar` class is a custom widget that displays a bottom navigation bar with
/// icons and text.
class CustomBottomNavigationBar extends StatelessWidget {
  const CustomBottomNavigationBar({
    super.key,
    required this.selectedIndexOfBottomNavigationBar,
  });

  final int selectedIndexOfBottomNavigationBar;

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Theme.of(context).bottomNavigationBarTheme.backgroundColor ?? ColorManager.darkSecondaryColor,
        borderRadius: BorderRadiusManager.commonAllBorderRadius,
      ),
      child: GNav(
        activeColor: Theme.of(context).bottomNavigationBarTheme.selectedItemColor ?? ColorManager.activeColor,
        gap: 8,
        selectedIndex: selectedIndexOfBottomNavigationBar,
        onTabChange: (index) {
          Provider.of<RootProvider>(context, listen: false).selectedIndexOfBottomNavigationBar = index;
        },
        tabs: const [
          GButton(
            icon: FontAwesomeIcons.house,
            text: TextManager.textHome,
          ),
          GButton(
            icon: FontAwesomeIcons.magnifyingGlass,
            text: TextManager.textSearch,
          ),
          GButton(
            icon: FontAwesomeIcons.locationCrosshairs,
            text: TextManager.textCurrent,
          ),
        ],
      ),
    );
  }
}

root_provider.dart file:

class RootProvider extends ChangeNotifier {
  int _selectedIndexOfBottomNavigationBar = 0;
  bool _isNight = true;

  int get selectedIndexOfBottomNavigationBar => _selectedIndexOfBottomNavigationBar;
  bool get isNight => _isNight;

  set selectedIndexOfBottomNavigationBar(int value) {
    _selectedIndexOfBottomNavigationBar = value;
    notifyListeners();
  }

  void changeIsNight() {
    _isNight = !_isNight;
    notifyListeners();
  }
}
its-me-mahmud
  • 690
  • 1
  • 7
  • 14
TheSylar
  • 48
  • 7

1 Answers1

0

I solved the problem. The error occurred because I used initialLocation in gorouter and not consumer.

main.dart file

import 'package:f_weather/product/components/custom_bottom_navigation_bar.dart';
import 'package:f_weather/product/constants/enums/navigate_routes_enum.dart';
import 'package:f_weather/product/constants/path_of_assets/lottie_url.dart';
import 'package:f_weather/product/extensions/navigate_routes_extension.dart';
import 'package:f_weather/product/extensions/padding_extension.dart';
import 'package:f_weather/product/init/navigator/navigator_manager.dart';
import 'package:f_weather/product/init/navigator/navigator_routes.dart';
import 'package:f_weather/product/init/theme/utility/padding_manager.dart';
import 'package:f_weather/product/init/theme/utility/theme_data_manager.dart';
import 'package:f_weather/product/state/root_provider.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';

void main() => runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => RootProvider()),
        ],
        child: const MyApp(),
      ),
    );

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      debugShowCheckedModeBanner: false,
      theme: CustomThemeData().lightTheme,
      darkTheme: CustomThemeData().darkTheme,
      themeMode: ThemeMode.dark,
      routerConfig: NavigatorRoutes().router,
    );
  }
}

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

  @override
  State<Root> createState() => _RootState();
}

class _RootState extends State<Root> with SingleTickerProviderStateMixin {
  late final AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
    );
  }

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

  @override
  Widget build(BuildContext context) {
    // debugPrint("Root build");
    return Consumer<RootProvider>(
      builder: (_, value, __) {
        return Scaffold(
          appBar: AppBar(
            title: InkWell(
              child: const Text("go sample"),
              onTap: () => NavigatorManager.instance.go(
                context,
                route: NavigateRoutes.sample.withParaph,
              ),
            ),
            actions: [
              SizedBox(
                width: 90,
                height: 80,
                child: _changeThemeToggleButton(context),
              ).onlyPadding(right: PaddingManager.commonHorizontal),
            ],
          ),
          body: Center(
            child: NavigatorRoutes().navPages.elementAt(value.selectedIndexOfBottomNavigationBar),
          ).symmetricpadding(
            horizontal: PaddingManager.commonHorizontal,
            vertical: PaddingManager.commonVertical,
          ),
          bottomNavigationBar: const CustomBottomNavigationBar().onlyPadding(
            left: PaddingManager.horizontalOfBottomNavigationBar,
            right: PaddingManager.horizontalOfBottomNavigationBar,
            bottom: PaddingManager.onlyBottomOfBottomNavigationBar,
            top: PaddingManager.onlyTopOfBottomNavigationBar,
          ),
        );
      },
    );
  }

  Consumer _changeThemeToggleButton(BuildContext context) {
    return Consumer<RootProvider>(
      builder: (_, value, __) {
        return InkWell(
          onTap: () {
            Provider.of<RootProvider>(context, listen: false).changeIsNight();
            debugPrint(value.isNight.toString());
            if (!value.isNight) {
              _animationController.animateTo(0.5);
            } else {
              _animationController.animateTo(-0.5);
            }
          },
          child: Lottie.asset(
            LottieUrls.pathOfToggleThemeButton,
            fit: BoxFit.cover,
            controller: _animationController,
            onLoaded: (composition) {
              _animationController
                ..duration = composition.duration
                ..value = 0.5;
            },
          ),
        );
      },
    );
  }
}

custom_buttom_navigation_bar.dart file

import 'package:f_weather/product/state/root_provider.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_nav_bar/google_nav_bar.dart';

import 'package:f_weather/product/constants/texts/text_manager.dart';
import 'package:f_weather/product/init/theme/utility/border_radius_manager.dart';
import 'package:f_weather/product/init/theme/utility/color_manager.dart';
import 'package:provider/provider.dart';

/// The `CustomBottomNavigationBar` class is a custom widget that displays a bottom navigation bar with
/// icons and text.
class CustomBottomNavigationBar extends StatelessWidget {
  const CustomBottomNavigationBar({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Theme.of(context).bottomNavigationBarTheme.backgroundColor ?? ColorManager.darkSecondaryColor,
        borderRadius: BorderRadiusManager.commonAllBorderRadius,
      ),
      child: Consumer<RootProvider>(
        builder: (_, value, __) {
          return GNav(
            activeColor: Theme.of(context).bottomNavigationBarTheme.selectedItemColor ?? ColorManager.activeColor,
            gap: 8,
            selectedIndex: value.selectedIndexOfBottomNavigationBar,
            onTabChange: (index) {
              Provider.of<RootProvider>(context, listen: false).selectedIndexOfBottomNavigationBar = index;
            },
            tabs: const [
              GButton(
                icon: FontAwesomeIcons.house,
                text: TextManager.textHome,
              ),
              GButton(
                icon: FontAwesomeIcons.magnifyingGlass,
                text: TextManager.textSearch,
              ),
              GButton(
                icon: FontAwesomeIcons.locationCrosshairs,
                text: TextManager.textCurrent,
              ),
            ],
          );
        },
      ),
    );
  }
}

navigation_routes.dart file

import 'package:f_weather/main.dart';
import 'package:f_weather/product/constants/enums/navigate_routes_enum.dart';
import 'package:f_weather/product/extensions/navigate_routes_extension.dart';
import 'package:f_weather/product/views/current_place.dart';
import 'package:f_weather/product/views/home.dart';
import 'package:f_weather/product/views/sample.dart';
import 'package:f_weather/product/views/search_places.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

/// The code defines a class called `NavigatorRoutes` that encapsulates the navigation routes and pages
/// for a Flutter application.
final class NavigatorRoutes {
  /// The code is creating an instance of the `NavigatorRoutes` class and initializing its `_router`
  /// property with a `GoRouter` object. The `GoRouter` object is configured with the following
  /// parameters:
  NavigatorRoutes()
      : _router = GoRouter(
          // initialLocation: NavigateRoutes.root.justParaph,
          routes: _items,
          errorBuilder: (context, state) => const Scaffold(
            body: Center(
              child: Text('Page not found'),
            ),
          ),
        );

  final GoRouter _router;

  /// The `static final _items` is a list of `GoRoute` objects that define the navigation routes and
  /// their corresponding builders. Each `GoRoute` object represents a specific route in the application.
  static final _items = [
    GoRoute(
      path: NavigateRoutes.root.justParaph,
      name: NavigateRoutes.root.name,
      builder: (context, state) => const Root(),
    ),
    GoRoute(
      path: NavigateRoutes.sample.withParaph,
      builder: (context, state) => const Sample(),
    ),
  ];
  static const List<Widget> _navPages = [
    Home(),
    SearchPlaces(),
    CurrentPlace(),
  ];

  GoRouter get router => _router;
  List<Widget> get navPages => _navPages;
}

TheSylar
  • 48
  • 7