I'm trying to avoid rebuilding FutureBuilder
in flutter. I have tried solution suggested in below Q's.
How to parse JSON only once in Flutter
Flutter Switching to Tab Reloads Widgets and runs FutureBuilder
still my app fires API every time I navigate to that page. Please point me where I'm going wrong.
Util.dart
//function which call API endpoint and returns Future in list
class EmpNetworkUtils {
...
Future<List<Employees>> getEmployees(data) async {
List<Employees> emps = [];
final response = await http.get(host + '/emp', headers: { ... });
final responseJson = json.decode(response.body);
for (var empdata in responseJson) {
Employees emp = Employees( ... );
emps.add(emp);
}
return emps;
}
}
EmpDetails.dart
class _EmpPageState extends State<EmpPage>{
...
Future<List<Employees>>_getAllEmp;
@override
initState() {
_getAllEmp = _getAll();
super.initState();
}
Future <List<Employees>>_getAll() async {
_sharedPreferences = await _prefs;
String authToken = AuthSessionUtils.getToken(_sharedPreferences);
return await EmpNetworkUtils().getEmployees(authToken);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar( ... ),
body: Container(
child: FutureBuilder(
future: _getAllEmp,
builder: (BuildContext context, AsyncSnapshot snapshot) { ... }
)))
}
}
Update:
I'm using bottomNavigationBar
in my app, from which this page is loaded.
You are calling your getEmployees
function in initState
, which is meant to be called every time your widget is inserted into the tree. If you want to save the data after calling your function the first time, you will have to have a widget that persists.
An easy implementation would be using an InheritedWidget
and a data class:
class InheritedEmployees extends InheritedWidget {
final EmployeeData employeeData;
InheritedEmployees({
Key key,
@required Widget child,
}) : assert(child != null),
employeeData = EmployeeData(),
super(key: key, child: child);
static EmployeeData of(BuildContext context) => (context.inheritFromWidgetOfExactType(InheritedEmployees) as InheritedEmployees).employeeData;
@override
bool updateShouldNotify(InheritedEmployees old) => false;
}
class EmployeeData {
List<Employees> _employees;
Future<List<Employees>> get employees async {
if (_employees != null) return _employees;
_sharedPreferences = await _prefs;
String authToken = AuthSessionUtils.getToken(_sharedPreferences);
return _employees = await EmpNetworkUtils().getEmployees(authToken);
}
}
Now, you would only have to place your InheritedEmployees
somewhere that will not be disposed, e.g. about your home page, or if you want, even about your MaterialApp
(runApp(InheritedEmployees(child: MaterialApp(..));
). This way the data is only fetched once and cached after that. You could also look into AsyncMemoizer
if that suits you better, but the example I provided should work fine.
Now, you will want to call this employees
getter in didChangeDependencies
because your _EmpPageState
is dependent on InheritedEmployees
and you need to look that up, which cannot happen in initState
:
class _EmpPageState extends State<EmpPage>{
Future<List<Employees>>_getAllEmp;
@override
void didChangeDependencies() {
_getAllEmp = InheritedEmployees.of(context).employees;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar( ... ),
body: Container(
child: FutureBuilder(
future: _getAllEmp,
builder: (BuildContext context, AsyncSnapshot snapshot) { ... }
)))
}
}
I mentioned that your State
is now dependent on your InheritedWidget
, but that does not really matter as updateShouldNotify
always returns false
(there are not going to be any additional builds).