I have a home page called home.dart, where I have defined my other pages like so (I'm using a bottom navbar):
`
late final List<Widget> _pageOptions;
#override
void initState() {
appBarColor = colors[4];
initializeCoins();
_pageOptions = [
ProScreen(Values.coins.toString()),
CatScreen(),
NotScreen(),
FavScreen(),
HomeScreen(),
];
super.initState();
}
`
I'm receiving some data from an API and showing it in home.dart's appBar and it works fine. the thing is when I'm trying to show the same thing in ProScreen (passing the same variable I'm showing in appbar to ProScreen's constructor) it doesn't work and shows it's defaut values instead (0)
What should I do?
class Values{
static int coins = 0;
...
}
Future<void> initializeCoins() async {
_prefs =await SharedPreferences.getInstance();
var response = await http.get(Uri.parse(Values.url+Values.user_endpoint),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${_prefs?.getString('token')}',
}
);
setState(() {
coinsLoaded = true;
Values.coins = (jsonDecode(response.body)['user']['score']);
});
}
I think this should work
Create a async function that prepare pages (initPages), it will wait until Values initialize, and after that do a setState and create your pages
Like this:
late final List<Widget> _pageOptions;
#override
void initState() {
initPages();
super.initState();
}
initPages() async {
appBarColor = colors[4];
await initializeCoins();
setState(() {
_pageOptions = [
ProScreen(Values.coins.toString()),
CatScreen(),
NotScreen(),
FavScreen(),
HomeScreen(),
];
});
}
That's because you're passing different value than you think. When you download data it works correctly the first time because you're using data received, but when creating new screen you take static value that you've created previously which is 0.
You explicitly state that this is a new 'instance' of the class Values when doing Values.coins.toString().
Try returning int that you're receiving Future<int> initializeCoins() async { and return jsonDecode(response.body)['user']['score']. With this you can do something like that:
#override
void initState() async {
appBarColor = colors[4];
int coins = await initializeCoins();
_pageOptions = [
ProScreen(coins.toString()),
CatScreen(),
NotScreen(),
FavScreen(),
HomeScreen(),
];
super.initState();
}
Related
I have a code like following in books_overview.dart:
#override
void initState() {
Provider.of<Books>(context).fetchAndSetBooks();
Future.delayed(Duration.zero).then((_) {
Provider.of<Books>(context).fetchAndSetBooks();
});
super.initState();
}
And a code like below within books.dart
Future<void> fetchAndSetBooks([bool filterByUser = false]) async {
final filterString =
filterByUser ? 'orderBy="creatorId"&equalTo="$userId"' : '';
var url = Uri.parse(
'https://flutter-update.firebaseio.com/Books.json?auth=$authToken&$filterString');
try {
final response = await http.get(url);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
if (extractedData == null) {
return;
}
}
And I get the following error:
FlutterError
(dependOnInheritedWidgetOfExactType<_InheritedProviderScope<Books?>>()
or dependOnInheritedElement() was called before
_BooksOverviewScreenState.initState() completed.
At this line of the code inside ``books_overview.dart`:
Provider.of<Books>(context).fetchAndSetBooks();
First of all, I would like to know is this a good way of using provider package(As the origin of the code is from a Flutter tutorial course)?
Secondly I like to know what is the problem and how can I fix it?
PS: I tried to change the first code as following but it didn't help:
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
Provider.of<Contests>(context).fetchAndSetContests();
});
}
I need to persist value from range slider to shared preferences, when user leaves page with sliders, it will still save value, not without resetting to default settings (default is 1).
I am trying to make things like that:
#override
void initState() {
// _loadSlider();
super.initState();
}
#override
void dispose() {
_debounce?.cancel();
super.dispose();
}
var _currentRangeValues = const RangeValues(1, 16);
void _loadSlider() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_currentRangeValues = (prefs.getStringList('sliderGain') ??
[
_currentRangeValues.start.round().toString(),
_currentRangeValues.end.toString()
]) as RangeValues;
});
}
// change slider value to value
void _changeSlider(RangeValues value) {
setState(() {
_currentRangeValues = value;
});
}
// store slider value
void _storeSlider() async {
final prefs = await SharedPreferences.getInstance();
prefs.setStringList('sliderGain', [
_currentRangeValues.start.round().toString(),
_currentRangeValues.end.round().toString()
]);
}
But I'm getting an error
RangeValues is not subtype of type List
How to resolve this issue?
I found what the issue was about my slider and attempts to save data from it to sharedprefernces. So it needs to convert to List after declaring the range value variable. After that, I made small changes in the code and put data from the declared list into the get string, and after that everything worked. Thanks to the previous commenter for the tip.
void _loadSlider() async {
final prefs = await SharedPreferences.getInstance();
List<String> valuesString = [currentRange.start.round().toString(), currentRange.end.round().toString() ];
setState(() {
valuesString = (prefs.getStringList('sliderGain') ??
[
valuesString.toString()
]);
print(valuesString);
});
}
I am writing a simeple todo list app, the todo item stored in sqlite
sqflite: ^2.0.0+3 right now. I want to load the sqlite todo data before loading the flutter page, this is my initial code looks like in flutter:
class _HomePageState extends State<HomePage> {
GlobalKey _inputViewKey = GlobalKey();
List<Todo> _todos = [];
#override
void initState() {
var _db = DBProvider.db;
_todos = await _db.getAllTodo();
super.initState();
}
}
and this is the function to load data from database:
Future<List<Todo>> getAllTodo() async {
final db = await database;
var result = await db.query('Todo');
return result.map((it) => Todo.fromJson(it)).toList();
}
the IDE told that I should add async in the initial function. When I add the async function, the initial function could not work. What should I do to make it work? how to initial the async data before the HomePage?
You cant mark async on inistate. You can try this
class _HomePageState extends State<HomePage> {
GlobalKey _inputViewKey = GlobalKey();
List<Todo> _todos = [];
#override
void initState() {
var _db = DBProvider.db;
getAllTodo();
super.initState();
}
}
And in the method
getAllTodo() async {
final db = await database;
var result = await db.query('Todo');
_todos = result.map((it) => Todo.fromJson(it)).toList();
setState((){});
}
I have a getx controller and a method to update data in the database, I just wonder that how can I refresh of update the UI constantly after fetching the API? Here is my controller
class AdditionalContactController extends GetxController {
var additionalContactList = <AdditionalContact>[].obs;
var isLoading = true.obs;
UserController _userController = Get.find();
#override
void onInit() {
super.onInit();
_userController.getMail().then((value) async {
await _userController.getUser(value);
getAdditionalContacts(_userController.user.value.id);
});
}
//Update contact
Future<bool> updateContact({...}) async {
var response = await http.post(
Uri.parse(
"https://..."),
body: {
...
});
var jsonData = jsonDecode(response.body);
if (jsonData == "failed") {
return false;
}
return true;
}
}
you can use the ever worker to call a method that executes every time an Rx have assigned with a new value:
class AdditionalContactController extends GetxController {
var additionalContactList = <AdditionalContact>[].obs;
#override
void onInit() {
super.onInit();
ever(additionalContactList, methodToExecute)
});
}
methodToExecute(list) {
/* Code that will execute every time the additionalContactList changes */
}
now everytime additionalContactList is changed like as example if we assign a new List to it:
additionalContactList.value = [];
Then the methodToExecute() will be executed automatically, and will do every single time.
I have been trying to make a app in flutter where an api is called and data is updated in TextField
Used provider for state management, here is the code for it.
class ProfileProvider with ChangeNotifier {
var profileData;
String _url = "http://10.0.2.2:3000/api/v1/user/loggedin_user";
void getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var data = await http.get(
_url,
headers: {
"accept": "application/json",
"content-type": "application/json",
'Token': token,
},
);
var infoOfPerson = json.decode(data.body);
profileData = new ProfileObject(
name: infoOfPerson['name'],
mobile: infoOfPerson['mobile'],
email: infoOfPerson['email'],
role: infoOfPerson['role'],
);
notifyListeners();
}
ProfileObject get profileInfo {
return profileData;
}
}
I am getting the data fine, now i have to show it in the UI, but sometime data is populated, sometime its not. Can someone please point me the right direction why this is happening.
Here is the code for UI.
class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final emailController = TextEditingController(text: '');
final nameController = TextEditingController(text: '');
final mobileController = TextEditingController(text: '');
var _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
final profileData = Provider.of<ProfileProvider>(context);
profileData.getData();
if (profileData.profileInfo != null) {
emailController.text = profileData.profileInfo.name;
nameController.text = profileData.profileInfo.email;
mobileController.text = profileData.profileInfo.mobile;
}
_isInit = false;
super.didChangeDependencies();
}
}
#override
Widget build(BuildContext context) {
final profileData = Provider.of<ProfileProvider>(context);
return Scaffold(
drawer: NavigationDrawer(),
body: profileData.profileInfo == null
? Center(
child: CircularProgressIndicator(),
)
: Builder(
builder: (context) => SingleChildScrollView(
child: Padding(.....
Below the padding, there is normal TextField, can someone tell me why the data is being populated sometime and sometime its coming empty, even I wrapped it with CircularProgressIndicator() and a check the notifyListeners(); is not working there. The loader is not being shown and data is not being loaded.
Thanks
for StatelessWidget.
Inside the build method use:
Future.microtask(() async {
context.read<SomeProvider>().fetchSomething();
});
For StatefulWidgets if you want to call it once. Do this inside the initState() or didChangeDependencies (better if the latter). This will be called at the end of the frame which means after the build or rendering finishes..
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<SomeProvider>().fetchSomething();
});
}
EDIT: WidgetsBinding will also work on build. I forgot on why I used microtask lol
i've created a function which called nextTick, i call it in initState and it works for now, but want to see others method
void nextTick(Function callback, [int milliseconds = 0]) {
Future.delayed(Duration(milliseconds: 0)).then((_) {
callback();
});
}
then use it like below
#override
void initState() {
super.initState();
nextTick((){
ProfileProvider profileProvider = Provider.of<ProfileProvider>(context);
profileProvider.getProfile();
});
}
Edit: i store couple of variables to manage them on ui, like isLoading, hasError and errorMessage. Here is my provider class
class ProfileProvider extends ChangeNotifier {
bool _hasError = false;
bool _isLoading = true;
String _errorMsg = '';
Profile _profileResponse;
bool get hasError => _hasError;
bool get isLoading => _isLoading;
String get errorMsg => _errorMsg;
Profile get profileResponse => _profileResponse;
Future<void> getProfile() async {
this.setLoading = true;
this.setError = false;
this.setErrorMsg = '';
try {
await dio.post('$api/p/view', data: {}).then((res) {
print(res.data);
_profileResponse = Profile.fromJson(jsonDecode(res.data));
print(_profileResponse.data);
notifyListeners();
}).whenComplete(() {
this.setLoading = false;
});
} catch (e) {
this.setError = true;
this.setErrorMsg = '$e';
}
this.setLoading = false;
}
set setError(bool val) {
if (val != _hasError) {
_hasError = val;
notifyListeners();
}
}
set setErrorMsg(String val) {
if (val != null && val != '') {
_errorMsg = val;
notifyListeners();
}
}
set setLoading(bool val) {
_isLoading = val;
notifyListeners();
}
}