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?
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.