Home > Enterprise >  Flutter - My widgets keep getting rebuilt and making http request every time
Flutter - My widgets keep getting rebuilt and making http request every time

Time:11-23

In my app I have an index screen with some bottom nav that can show or go to another screen on click using setState to change the _selectedIndex and the screen are in a list

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

  @override
  State<IndexPage> createState() => _IndexPageState();
}

class _IndexPageState extends State<IndexPage> {
  int _selectedIndex = 0;
  final List<Widget> _widgetOptions = <Widget>[
    const HomeScreen(),
    const ExploreScreen(),
    const InvestScreen(),
    const WalletScreen(),
    const MoreScreen(),
  ];

  @override
  void initState() {
    connectRequiredApi();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: _widgetOptions[_selectedIndex],
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
            icon: _selectedIndex == 0
                ? const Icon(IconlyBold.home, size: 22)
                : const Icon(IconlyLight.home, size: 22),
            label: "Home",
          ),

         //.......//

          BottomNavigationBarItem(
            icon: _selectedIndex == 4
                ? const Icon(IconlyBold.category, size: 22)
                : const Icon(IconlyLight.category, size: 22),
            label: 'More',
          ),
        ],
        currentIndex: _selectedIndex,
        //...
        onTap: itemTapped,
      ),
    );
  }

  void itemTapped(int index) {
    if (index == 2) {
      showToast(context, "Coming Soon");
    } else {
      setState(() {
        _selectedIndex = index;
      });
    }
  }

  void connectRequiredApi() async {
    await Get.find<MapController>().checkLocationPermission();
    //...//
  }
}

And in my HomeScreen(); I have an initState that calls an http request to my backend and it calls it only once when am on the homescreen. But the problem am having now is that whenever I click on the bottom nav to switch screen and then go back to my HomeScreen() it gets rebuilt and calls the initState again making another http request showing my loading shimmer even after i have tried to maintain the state of the page

HomeScreen();

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen>
    with AutomaticKeepAliveClientMixin<HomeScreen> {


  @override
  void initState() {
    getLocation();
    super.initState();
  }

  getLocation() async {
    await Get.find<PropertyController>().getFeaturedProperty();
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    ColorScheme theme = Theme.of(context).colorScheme;
    return SingleChildScrollView(
      padding: const EdgeInsets.all(20),
      child: GetBuilder<PropertyController>(
          id: "home_page.dart",
          builder: (propertyController) {
            return Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
              
             //.....//

                propertyController.homeLoading
                    ? ListView.builder(
                        itemCount: 4,
                        shrinkWrap: true,
                        physics: const NeverScrollableScrollPhysics(),
                        itemBuilder: (ctx, idx) {
                          return const GFShimmer(child: HomeShimmer());
                        })
                    : propertyController.featuredPropertyList.isEmpty
                        ? const SizedBox()
                        : ListView.builder(
                            itemCount:
                                propertyController.featuredPropertyList.length,
                            shrinkWrap: true,
                            physics: const NeverScrollableScrollPhysics(),
                            itemBuilder: (ctx, idx) {
                              return HouseCard(
                                property: propertyController
                                    .featuredPropertyList[idx],
                              );
                            }),

              //...//

                propertyController.homeLoading
                    ? ListView.builder(
                        itemCount: 4,
                        shrinkWrap: true,
                        physics: const NeverScrollableScrollPhysics(),
                        itemBuilder: (ctx, idx) {
                          return const GFShimmer(child: HomeShimmer());
                        })
                    : propertyController.popularPropertyList.isEmpty
                        ? const NotFound(
                            icon: Icons.domain_disabled_outlined,
                            message: "No property listed",
                          )
                        : SizedBox(
                            height: 240,
                            child: ListView.builder(
                              shrinkWrap: true,
                              itemCount:
                                  propertyController.popularPropertyList.length,
                              scrollDirection: Axis.horizontal,
                              itemBuilder: (context, index) {
                                return PopularHouse(
                                  propertyModel: propertyController
                                      .popularPropertyList[index],
                                );
                              },
                            ),
                          )
              ],
            );
          }),
    );
  }

  @override
  bool get wantKeepAlive => true;
}

My PropertyController

class PropertyController extends GetxController {
  PropertyRepo propertyRepo = PropertyRepo();
  PropertyModel propertyModel = PropertyModel();
  PropertyModel apartmentModel = PropertyModel();

  final List<PropertyModel> _featuredPropertyList = [];
  final List<PropertyModel> _popularPropertyList = [];
  bool homeLoading = true;

  List<PropertyModel> get featuredPropertyList => _featuredPropertyList;
  List<PropertyModel> get popularPropertyList => _popularPropertyList;

//....//

  Future<void> getFeaturedProperty() async {
    homeLoading = true;
    update(["home_page.dart", "explore_prop_page.dart"]);
    ResponseModel response = await propertyRepo.getFeaturedProperties();
    if (response.statusCode == 200) {
      _featuredPropertyList.clear();
      _featuredPropertyList.addAll((response.data as List)
          .map((e) => PropertyModel.fromJson(e))
          .toList());
    }
    await getPopularProperties();
    homeLoading = false;
    update(["home_page.dart", "explore_prop_page.dart"]);
  }

  Future<void> getPopularProperties() async {
    ResponseModel response = await propertyRepo.getPopularProperties();
    if (response.statusCode == 200) {
      if (kDebugMode) {
        print(response.data);
      }
      _popularPropertyList.clear();
      _popularPropertyList.addAll((response.data['properties'] as List)
          .map((e) => PropertyModel.fromJson(e))
          .toList());
    }
  }
}

Please how can I solve this issue I have tried maintaining state in homescreen but it's still getting called every time. If you need more explanation, please let me know

CodePudding user response:

The problem is that:

  • When you move between pages you change the built widget and this re-calls the initState

to solve this issues use PageView() widget with PageController

Your code will be something like this:

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

  @override
  State<IndexPage> createState() => _IndexPageState();
}

class _IndexPageState extends State<IndexPage> {
  int _selectedIndex = 0;
  @override
  void initState() {
    connectRequiredApi();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Pageview(
          controller: pageController,
          children:[
            const HomeScreen(),
            const ExploreScreen(),
            const InvestScreen(),
            const WalletScreen(),
            const MoreScreen(),]
         ],),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
            icon: _selectedIndex == 0
                ? const Icon(IconlyBold.home, size: 22)
                : const Icon(IconlyLight.home, size: 22),
            label: "Home",
          ),

         //.......//

          BottomNavigationBarItem(
            icon: _selectedIndex == 4
                ? const Icon(IconlyBold.category, size: 22)
                : const Icon(IconlyLight.category, size: 22),
            label: 'More',
          ),
        ],
        currentIndex: _selectedIndex,
        //...
        onTap: itemTapped,
      ),
    );
  }

  void itemTapped(int index) {
    if (index == 2) {
      showToast(context, "Coming Soon");
    } else {
      setState(() {
        _selectedIndex = index;
      });
    }
  }

  void connectRequiredApi() async {
    await Get.find<MapController>().checkLocationPermission();
    //...//
  }
}
  • Related