Loading data with Provider: Flutter

824 Views Asked by At

I am working on a Flutter App in which I want to load data using a REST API.
The provider class is as follows-
CategoryProvider-

class CategoryProvider with ChangeNotifier {
  SharedPreferences? sharedPreferences;
  late LoadingStatus loadingStatus;
  late CategoryService _categoryService;
  List<Category>? allCategories;
  CategoryProvider.initialze(SharedPreferences sf) {
    _initializePrefs(sf);
    loadingStatus = LoadingStatus.NOT_STARTED;
    _categoryService = CategoryService();
  }
  void _initializePrefs(SharedPreferences sf) {
    log('Initializing sharedPreferences');
    sharedPreferences = sf;
    log('Shared preference initialized');
  }

  void fetchAllCategories() async {
    allCategories = [];
    loadingStatus = LoadingStatus.LOADING;
    Map<String, dynamic> result = await _categoryService.fetchAllCategories(
        token: sharedPreferences!.getString(BEARER_TOKEN) ?? 'null');
    if (result['code'] == '2000') {
      allCategories = categoryFromJson(result['data']);
    } else {
      log('No categories: code: $result');
      allCategories = [];
    }
    loadingStatus = LoadingStatus.COMPLETED;
    notifyListeners();
  }
}

UI Code-

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final List<Widget> _pages = [
    PersonalFeedPage(),
    ExplorePage(),
    ProfilePage(),
    SettingsPage(),
  ];
  late PageController _pageController;
  int _selectedIndex = 0;
  @override
  void initState() {
    super.initState();
    _pageController = PageController();
  }

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

  _onTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
    _pageController.jumpToPage(index);
  }

  void onPageChanged(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    final categoryProvider = Provider.of<CategoryProvider>(context);
    if (categoryProvider.loadingStatus == LoadingStatus.NOT_STARTED) {
      // log('Bottom Navigation: loading user for email: ${userProvider.sharedPreferences!.getString(EMAIL)} ');
      log("Bottom Navigation: fetching all categories");
      categoryProvider.fetchAllCategories();
    }
    return Scaffold(
      body: (!(categoryProvider.loadingStatus == LoadingStatus.LOADING ||
              categoryProvider.loadingStatus == LoadingStatus.NOT_STARTED))
          ? PageView(
              children: _pages,
              controller: _pageController,
              onPageChanged: onPageChanged,
            )
          : Center(
              child: Container(
                height: 100,
                width: 100,
                child: CustomLoadingIndicator(),
              ),
            ),
      bottomNavigationBar:
          (!(categoryProvider.loadingStatus == LoadingStatus.LOADING ||
                  categoryProvider.loadingStatus == LoadingStatus.NOT_STARTED))
              ? BottomNavigationBar(
                  currentIndex: _selectedIndex,
                  onTap: _onTapped,
                  items: [
                    const BottomNavigationBarItem(
                      label: "Feed",
                      icon: Icon(FontAwesomeIcons.rss),
                    ),
                    const BottomNavigationBarItem(
                      label: "Explore",
                      icon: Icon(FontAwesomeIcons.borderAll),
                    ),
                    const BottomNavigationBarItem(
                      label: "Profile",
                      icon: Icon(FontAwesomeIcons.user),
                    ),
                    const BottomNavigationBarItem(
                      label: "Settings",
                      icon: Icon(FontAwesomeIcons.gears),
                    ),
                  ],
                )
              : null,
    );
  }
}

What I want to do-

  • The HomePage consists of a bottom navigation bar which can be used to navigate between the four pages. Home Page is the first widget to be built when app is opened.

  • Now, when the app is opened, I want to fetch all the data using the fetchAllCategories() method (which are stored in the allCategories variable).

  • The same fetchAllCategories() might be called from other parts of app to refresh data.

My approach-

  • I am using the loadingStatus variable in the CategoryProvider to keep track of the data loaded.

  • If the data is getting loaded, it will be set as LOADING, else if not started fetching then as NOT_STARTED else if loaded then COMPLETED.

  • The widgets will get built accordingly.

My Question-
I am fetching data in the build() method of the Home Page because I can't access context outside it. So, will this approach be efficient in loading data or is there some more efficient approach for this functionality? Although, this would work but I am not sure this is efficient and correct approach when I have to re-fetch the data?

1

There are 1 best solutions below

1
w461 On

With ObjectBox instead of SharedPreferences you could read the data in sync. This would make the code a bit cleaner. I am using BLoC and with this package I would load the data in a quite different pattern. However, with ObjectBox you might be able to streamline your code such that it doesn't matter.