Issue
I’m using flutter bloc pattern in my flutter app. I have bottom navigation bar with several tabs in one page. Two of them are using the same api call (the same State). When the user taps on 1 of them I call the api to get the data, but if the user taps on the other tab I want to get the data without calling the api again. How I can do that?
In my main page (dashboard) I have BlocBuilder to change the tabs and I create the Dashboard cubit in it
class DashboardPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<TabsBloc, AppTab>(
builder: (BuildContext context, AppTab activeTab) {
return Scaffold(
appBar: AppBar(
title: Text(DashboardHelpers.getTabLabel(activeTab)),
),
body: RepositoryProvider(
create: (BuildContext context) => DashboardRepository(),
child: BlocProvider<DashboardCubit>(
create: (BuildContext context) => DashboardCubit(
dashboardRepository: context.read<DashboardRepository>(),
authBloc: context.read<AuthBloc>(),
),
child: DashboardHelpers.getTabContent(activeTab),
),
),
bottomNavigationBar: TabSelector(
activeTab: activeTab,
onTabSelected: (tab) =>
BlocProvider.of<TabsBloc>(context).add(TabUpdated(tab))),
);
},
);
}
}
The tabs are View that are loaded as child. One of the views is View1. When I get the data I loaded in ContentView1
class View1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
BlocProvider.of<DashboardCubit>(context)..getDashboardDevices();
return BlocConsumer<DashboardCubit, DashboardState>(
listener: (BuildContext context, DashboardState state) {
if (state is DashboardError) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Text(state.message),
),
);
}
},
builder: (BuildContext context, DashboardState state) {
if (state is DevicesLoaded) {
return ContentView1(data: state.data);
} else if (state is DashboardLoading) {
return LoadingWidget();
} else if (state is DashboardError) {
return Container(
child: Center(
child: Text(state.message),
),
);
} else {
return Container();
}
},
);
}
}
and the View2 is almost the same. The data is absolutely the same and it is loaded in ContentView2 but it is a completly different widget than ContentView1
class View2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
BlocProvider.of<DashboardCubit>(context)..getDashboardDevices();
return BlocConsumer<DashboardCubit, DashboardState>(
listener: (BuildContext context, DashboardState state) {
if (state is DashboardError) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Text(state.message),
),
);
}
},
builder: (BuildContext context, DashboardState state) {
if (state is DevicesLoaded) {
return ContentView2(data: state.data);
} else if (state is DashboardLoading) {
return LoadingWidget();
} else if (state is DashboardError) {
return Container(
child: Center(
child: Text(state.message),
),
);
} else {
return Container();
}
},
);
}
}
The problem is that these two VIEWs are showing different data that comes from the same API endpoint.
How can I load the already gotten data when the user tabs from View1 to View2 without calling the API again.
Thanks!
Solution
You should be calling getDashboardDevices()
only once, for that you could create a DashboardInitialState
, when user clicks one of tabs, if the state is DashboardInitialState
you run getDashboardDevices()
and not always when the view is building. This way you will load data only once when one of the views is built and both of them will use the same data on loaded state.
There is View1 as example, try that with both views:
class View1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocConsumer<DashboardCubit, DashboardState>(
listener: (BuildContext context, DashboardState state) {
if (state is DashboardError) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Text(state.message),
),
);
}
},
builder: (BuildContext context, DashboardState state) {
if(state is DashboardInitialState) {
BlocProvider.of<DashboardCubit>(context)..getDashboardDevices();
return LoadingWidget();
} else if (state is DevicesLoaded) {
return ContentView1(data: state.data);
} else if (state is DashboardLoading) {
return LoadingWidget();
} else if (state is DashboardError) {
return Container(
child: Center(
child: Text(state.message),
),
);
} else {
return Container();
}
},
);
}
}
Answered By – NelsonThiago
Answer Checked By – Marie Seifert (FlutterFixes Admin)