Flutter, problem using InitState method and problem using setState - flutter

I have tried many things to get to call an API and get data, this has been satisfactory but I have not been able to do it from the correct method, when I do it from build it works perfectly but it is not the right place, when I try to do it in initState it simply does not it works, it doesn't even execute the print ();
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class Post {
final List<dynamic> data;
Post({this.data});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
data: json['response'],
);
}
}
class LoaderPublicity extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _LoaderPublicity();
}
}
class _LoaderPublicity extends State<LoaderPublicity> {
List allPublicity;
#override
void initState() {
super.initState();
getPublicity().then((value) {
print(allPublicity);
});
print(allPublicity);
}
//Obtener todas las publicidades desde el api
Future<void> getPublicity() async {
var response = await http.post(
'http://contablenift.co:3008/consult/getGeneralPublicity',
body: {'actividad': "Turistico", 'location': "No"});
print('????????2');
if (response.statusCode == 200) {
setState(() {
allPublicity = Post.fromJson(json.decode(response.body)).data;
});
}
}
#override
Widget build(BuildContext context) {
getPublicity();
// TODO: implement build
return Container(
child: Column(
children: <Widget>[Text('Aqui va la publicidad')],
),
);
}
}
**
Solved the problem, after going to the entire development team we realized that my phone did not have internet, happy day**

The code below should work, I've added a callback in the initState method.
class LoaderPublicity extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _LoaderPublicity();
}
}
class _LoaderPublicity extends State<LoaderPublicity> {
List allPublicity;
#override
void initState() {
super.initState();
getPublicity().then((value) {
print(allPublicity);
print(value);
});
}
//Obtener todas las publicidades desde el api
Future<void> getPublicity() async {
var response = await http.post(
'http://contablenift.co:3008/consult/getGeneralPublicity',
body: {'actividad': "Turistico", 'location': "No"})
print('????????2');
if (response.statusCode == 200) {
setState(() {
allPublicity = Post.fromJson(json.decode(response.body)).data;
});
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[Text('Aqui va la publicidad')],
),
);
}
}

Related

Flutter GetX dependency Injection

I'm new to GetX flutter state management. I'm using two controllers, one for Login and other for Home data(fetching some restaurants data through API call). I'm having trouble in bindings. I'm using bindings in my app following GetX docs. But I'm unable to use it properly and getting error. Following is the code -:
main.dart
void main() async {
await GetStorage.init('My Storage');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flunkey Task',
getPages: [
GetPage(
name: '/',
page: () => LandingPage(),
binding: BindingsBuilder(() {
Get.lazyPut<LoginController>(() => LoginController());
})),
GetPage(
name: '/login',
page: () => LoginScreen(),
binding: BindingsBuilder(() {
Get.lazyPut<LoginController>(() => LoginController());
})),
GetPage(
name: '/home',
page: () => HomeScreen(),
binding: BindingsBuilder(() {
Get.lazyPut<HomeController>(() => HomeController());
}),
)
],
initialRoute: '/',
);
}
}
class LandingPage extends StatelessWidget {
LandingPage({Key? key}) : super(key: key);
final _controller = Get.find<LoginController>();
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Obx(() =>
_controller.isLoggedIn.value == true ? HomeScreen() : LoginScreen());
}
}
loginController.dart
class LoginController extends GetxController {
final box = GetStorage('My Storage');
var isLoggedIn = false.obs;
final formKey = GlobalKey<FormState>();
final usernameTED = TextEditingController();
final passwordTED = TextEditingController();
#override
void onInit() {
isLoggedIn(loginStatus);
super.onInit();
}
#override
void onClose() {
usernameTED.dispose();
passwordTED.dispose();
super.onClose();
}
String? checkUsername(String username) {
if (username.isEmpty || username.length < 3 || username.length > 11) {
return 'Username must have 3-11 characters';
}
return null;
}
String? checkPassword(String password) {
if (password.isEmpty || password.length < 3 || password.length > 11) {
return 'Password must have 3-11 characters';
}
return null;
}
Future<void> login() async {
if (!formKey.currentState!.validate()) {
return;
}
if ((usernameTED.text.trim() == 'flunkey' &&
passwordTED.text.trim() == 'password123') ||
(usernameTED.text.trim() == 'user' &&
passwordTED.text.trim() == 'password123')) {
formKey.currentState!.save();
await changeLoginStatus(true);
await saveUserName(usernameTED.text);
usernameTED.clear();
passwordTED.clear();
} else {
Get.snackbar('Login Error', 'User does not exists',
backgroundColor: Colors.red[400]);
}
}
void signOut() async {
await changeLoginStatus(false);
}
Future<void> changeLoginStatus(bool status) async {
await box.write('status', status);
isLoggedIn(status);
}
Future<void> saveUserName(String name) async {
await box.write('name', name);
}
bool get loginStatus => box.read('status') ?? false;
String get currentUserName => box.read('name') ?? '';
}
homeController.dart
class HomeController extends GetxController {
final _isLoading = false.obs;
final _restaurantData = <restau.Datum>[].obs;
#override
void onInit() {
getData();
super.onInit();
}
bool get isLoading => _isLoading.value;
List<restau.Datum> get getRestaurants => _restaurantData;
Future<void> getData() async {
try {
_isLoading(true);
var apiData = await RestaurantDataApiCall.getRestaurantData();
_restaurantData.value = apiData!.data.data;
_isLoading(false);
} catch (e, s) {
print(e);
print(s);
}
}
}
Following is the error I'm getting.
I'm using Get.find() on Login Screen and Get.find() on Home screen as following,
Please guide me how to properly use Bindings in GetX.
I don't like to bind the controllers on route.
I create a MainBind.dart and put inside this class all getx controllers.
class MainBinding implements Bindings {
#override
Future<void> dependencies() async{
Get.lazyPut<AppController>(() => AppController(), fenix: true);
}
}
And in my Main.dart :
void main() async{
WidgetsFlutterBinding.ensureInitialized();
MainBinding mainBinding = MainBinding();
await mainBinding.dependencies();
runApp(const MyApp());
}
In this way I'm sure that Controllers are binded.
But you can try use Put insted lazyPut too..
You can use StatefulWidget with state class which will contain your controller.
E.g.
StateClass bla, bla {
late final yourController = Get.put<YourController>();
#override
dispose() {
Get.delete<YourController>();
}
}
That's it!

Range Error (Index) Flutter GETX : invalid value

Here I have an error when I fetch data, I take a reference from youtube then I apply and use data from local but when I run it I get an error as I described. here is my source code
Controller.dart
class ProdukKonvensionalController extends GetxController {
var konvenList = <ProdukKonvenModel>[].obs;
var isLoading = true.obs;
#override
void onInit() {
super.onInit();
fetchKonven();
}
Future<void> fetchKonven() async {
final response =
await http.get(Uri.parse('http://192.168.100.207:8080/konven'));
if (response.statusCode == 200) {
ProdukKonvenModel _produkkonvenModel =
ProdukKonvenModel.fromJson(jsonDecode(response.body));
konvenList.add(ProdukKonvenModel(
kategoriNama: _produkkonvenModel.kategoriNama,
kategoriId: _produkkonvenModel.kategoriId,
kontenId: _produkkonvenModel.kontenId,
kontenMenu: _produkkonvenModel.kontenMenu,
kontenParent: _produkkonvenModel.kontenParent,
kontenUrl: _produkkonvenModel.kontenUrl,
));
isLoading.value = true;
} else {
Get.snackbar("Error Loading Data",
'Server Responded: ${response.statusCode}:${response.reasonPhrase.toString()}');
}
}
}
PageView.dart
class ProdukKonvensionalPage extends StatelessWidget {
const ProdukKonvensionalPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final _controller = Get.find<ProdukKonvensionalController>();
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${_controller.konvenList[0].kategoriNama}'),
],
)),
);
}
}
and the following shows an error
try this
var konvenList = <ProdukKonvenModel>[].obs;
var isLoading = true.obs;
#override
void onInit() {
super.onInit();
fetchKonven();
}
Future<void> fetchKonven() async {
final response = await http.get(Uri.parse('http://192.168.100.207:8080/konven'));
if (response.statusCode == 200) {
ProdukKonvenModel _produkkonvenModel =
ProdukKonvenModel.fromJson(jsonDecode(response.body));
_produkkonvenModel.forEach((element) => konvenList.add(element));
isLoading.value = true;
} else {
Get.snackbar("Error Loading Data",
'Server Responded:
${response.statusCode}:${response.reasonPhrase.toString()}');
}
}
}

Flutter Custom State Management

What I am trying to achieve is a small custom state management solution that I believe is powerful enough to run small and large apps. The core is based on the ValueNotifier and ValueListenable concepts in flutter. The data can be accessed anywhere in the app with out context since I am storing the data like this:
class UserData {
static ValueNotifier<DataLoader<User>> userData =
ValueNotifier(DataLoader<User>());
static Future<User> loadUserData() async {
await Future.delayed(const Duration(seconds: 3));
User user = User();
user.age = 23;
user.family = 'Naoushy';
user.name = 'Anass';
return user;
}
}
So by using UserData.userData you can use the data of the user whenever you want. Everything works fine until I encountered a problem of providing a child to my custom data consumer that rebuilds the widget when there is a new event fired. The DataLoader class looks like this:
enum Status { none, hasError, loading, loaded }
class DataLoader<T> {
Status status = Status.none;
T? data;
Object? error;
bool get hasError => error != null;
bool get hasData => data != null;
}
which is very simple. Now the class for consuming the data and rebuilding looks like this:
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:testing/utils/dataLoader/data_loader.dart';
class DataLoaderUI<T> extends StatefulWidget {
final ValueNotifier<DataLoader<T>> valueNotifier;
final Widget noneStatusUI;
final Widget hasErrorUI;
final Widget loadingUI;
final Widget child;
final Future<T> future;
const DataLoaderUI(
{Key? key,
required this.valueNotifier,
this.noneStatusUI = const Text('Data initialization has not started'),
this.hasErrorUI = const Center(child: Text('Unable to fetch data')),
this.loadingUI = const Center(
child: CircularProgressIndicator(),
),
required this.child,
required this.future})
: super(key: key);
#override
State<DataLoaderUI> createState() => _DataLoaderUIState();
}
class _DataLoaderUIState extends State<DataLoaderUI> {
Future startLoading() async {
widget.valueNotifier.value.status = Status.loading;
widget.valueNotifier.notifyListeners();
try {
var data = await widget.future;
widget.valueNotifier.value.data = data;
widget.valueNotifier.value.status = Status.loaded;
widget.valueNotifier.notifyListeners();
} catch (e) {
log('future error', error: e.toString());
widget.valueNotifier.value.error = e;
widget.valueNotifier.value.status = Status.hasError;
widget.valueNotifier.notifyListeners();
}
}
#override
void initState() {
super.initState();
log('init state launched');
if (!widget.valueNotifier.value.hasData) {
log('reloading or first loading');
startLoading();
}
}
//AsyncSnapshot asyncSnapshot;
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<DataLoader>(
valueListenable: widget.valueNotifier,
builder: (context, dataLoader, ui) {
if (dataLoader.status == Status.none) {
return widget.noneStatusUI;
} else if (dataLoader.status == Status.hasError) {
return widget.hasErrorUI;
} else if (dataLoader.status == Status.loading) {
return widget.loadingUI;
} else {
return widget.child;
}
});
}
}
which is also simple yet very effective. since even if the initState function is relaunched if the data is already fetched the Future will not relaunch.
I am using the class like this:
class TabOne extends StatefulWidget {
static Tab tab = const Tab(
icon: Icon(Icons.upload),
);
const TabOne({Key? key}) : super(key: key);
#override
State<TabOne> createState() => _TabOneState();
}
class _TabOneState extends State<TabOne> {
#override
Widget build(BuildContext context) {
return DataLoaderUI<User>(
valueNotifier: UserData.userData,
future: UserData.loadUserData(),
child: Text(UserData.userData.value.data!.name??'No name'));
}
}
The error is in this line:
Text(UserData.userData.value.data!.name??'No name'));
Null check operator used on a null value
Since I am passing the Text widget as an argument with the data inside it. Flutter is trying to pass it but not able to since there is no data yet so its accessing null values. I tried with a normal string and it works perfectly. I looked at the FutureBuilder widget and they use a kind of builder and also the ValueLisnableBuilder has a builder as an arguement. The problem is that I am not capable of creating something like it for my custom solution. How can I just pass the child that I want without having such an error and without moving the ValueLisnable widget into my direct UI widget?
I have found the solution.
Modify the DataLoaderUI class to this:
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:testing/utils/dataLoader/data_loader.dart';
class DataLoaderUI<T> extends StatefulWidget {
final ValueNotifier<DataLoader<T>> valueNotifier;
final Widget noneStatusUI;
final Widget hasErrorUI;
final Widget loadingUI;
final Widget Function(T? snapshotData) child;
final Future<T> future;
const DataLoaderUI(
{Key? key,
required this.valueNotifier,
this.noneStatusUI = const Text('Data initialization has not started'),
this.hasErrorUI = const Center(child: Text('Unable to fetch data')),
this.loadingUI = const Center(
child: CircularProgressIndicator(),
),
required this.child,
required this.future})
: super(key: key);
#override
State<DataLoaderUI<T>> createState() => _DataLoaderUIState<T>();
}
class _DataLoaderUIState<T> extends State<DataLoaderUI<T>> {
Future startLoading() async {
widget.valueNotifier.value.status = Status.loading;
widget.valueNotifier.notifyListeners();
try {
var data = await widget.future;
widget.valueNotifier.value.data = data;
widget.valueNotifier.value.status = Status.loaded;
widget.valueNotifier.notifyListeners();
} catch (e) {
log('future error', error: e.toString());
widget.valueNotifier.value.error = e;
widget.valueNotifier.value.status = Status.hasError;
widget.valueNotifier.notifyListeners();
}
}
#override
void initState() {
super.initState();
log('init state launched');
if (!widget.valueNotifier.value.hasData) {
log('reloading or first loading');
startLoading();
}
}
//AsyncSnapshot asyncSnapshot;
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<DataLoader<T>>(
valueListenable: widget.valueNotifier,
builder: (context, dataLoader, ui) {
if (dataLoader.status == Status.none) {
return widget.noneStatusUI;
} else if (dataLoader.status == Status.hasError) {
return widget.hasErrorUI;
} else if (dataLoader.status == Status.loading) {
return widget.loadingUI;
} else {
return widget.child(dataLoader.data);
}
});
}
}
and use it like this:
DataLoaderUI<User>(
valueNotifier: UserData.userData,
future: UserData.loadUserData(),
child: (user) {
return Text(user!.name ?? 'kk');
});
Take a look at my version of the same sort of state management approach here: https://github.com/lukehutch/flutter_reactive_widget

Why is the flutter setState not updating the list?

I have two api's from which I get data, I wanna check if any of the desired field match with each other data coming but it don't seem to work.
I have two api's from which I get data, I wanna check if any of the desired field match with each other data coming but it don't seem to work.
I have two api's from which I get data, I wanna check if any of the desired field match with each other data coming but it don't seem to work.
class Home extends StatefulWidget {
const Home({ Key? key }) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List compaints = [];
List found = [];
List match = [];
var u_imei;
var d_imei;
Future fetch() async {
http.Response response_one;
http.Response response_two;
response_one = await http.get(Uri.parse("https://script.google.com/macros/s/AKfycbxi9kN6NWvoFjkQZE1OVJDPpWmQeYk0V5hNfRKqXS19wjz86SYq_FoQ51fjNQY22bN4/exec"));
response_two = await http.get(Uri.parse("https://script.google.com/macros/s/AKfycbx20kfm1g4Hno9DzO1uccmLgmuIQBkXQcA9tnhcup873TsEMEy9ejszCluhf4FzW-YJqQ/exec"));
if(response_one == 200 && response_two == 200){
if(mounted){
setState(() {
compaints = jsonDecode(response_one.body);
found = jsonDecode(response_two.body);
u_imei = compaints[2];
d_imei = found[1];
if(d_imei == u_imei) {
if(mounted){
print("working");
setState(() {
match.add(d_imei);
});
}
}
});
}
}
}
#override
Widget build(BuildContext context) {
// fetchu();
// fetchd();
// check();
fetch();
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(compaints.length.toString()),
SizedBox(height: 20,),
Text(found.length.toString()),
],
),
);
}
}
There are several issues:
fetch is called in build, which causes rebuild loop. First step to move it to initState.
Response is compared to 200 (response_one == 200). There is property statusCode.
Parsing imei's is not correct. Responses:
[{time: 2022-07-03T16:07:15.491Z, name: Asif, imei: 1234, number: 9014580667}]
[{time: 2022-07-05T08:12:31.029Z, imei: 1234}]
So should be something like this:
u_imei = compaints[0]['imei'];
d_imei = found[0]['imei'];
Calling the fetch method inside the build will loop as the fetch method calls the setState(). Use initState() to call on the load or on refresh indicator while the user pulls to refresh or any other method.
class Home extends StatefulWidget {
const Home({ Key? key }) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List compaints = [];
List found = [];
List match = [];
var u_imei;
var d_imei;
#override
void initState() {
super.initState();
fetch();
}
#override
void dispose() {
super.dispose();
}
Future fetch() async {
http.Response response_one;
http.Response response_two;
response_one = await http.get(Uri.parse("https://script.google.com/macros/s/AKfycbxi9kN6NWvoFjkQZE1OVJDPpWmQeYk0V5hNfRKqXS19wjz86SYq_FoQ51fjNQY22bN4/exec"));
response_two = await http.get(Uri.parse("https://script.google.com/macros/s/AKfycbxEDXZAmieRWk-8kOX-07ta8Q4TIa9Lf_NAiArEWhaU4jXO8d_DM9Jwuc0DRIwmUpPh/exec"));
if(response_one.statusCode == 200 && response_two.statusCode == 200){
if(mounted){
setState(() {
compaints = jsonDecode(response_one.body);
found = jsonDecode(response_two.body);
u_imei = compaints[2];
d_imei = found[1];
if(d_imei == u_imei) {
if(mounted){
print("working");
setState(() {
match.add(d_imei);
});
}
}
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(compaints.length.toString()),
SizedBox(height: 20,),
Text(found.length.toString()),
],
),
);
}
}

I have a problem with getting a geolocation on flutter

I' am trying to get geolocation and then push it on other route, but it returns me smth like:
W/ActivityThread(27303): SCHED: razewp.covid19_20/.MainActivity [81, r=36ms, a=8ms, w=31795ms]
here's code:
its my first file loc_get.dart
import 'package:geolocator/geolocator.dart';
class LocationGetter {
var test;
LocationGetter({
this.test,
});
Future<void> getLoc() async {
final position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
}
}
this is my loc.dart:
import 'package:flutter/material.dart';
import 'package:covid19_20/pages/loc_get.dart';
class Location extends StatefulWidget {
#override
_LocationState createState() => _LocationState();
}
class _LocationState extends State<Location> {
void setupLoc() async {
LocationGetter location = LocationGetter(
test: '',
);
await location.getLoc();
Navigator.pushReplacementNamed(context, '/loading', arguments: {
'test': location.test,
});
}
#override
void initState() {
super.initState();
setupLoc();
}
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(
child: Text('Getting location...')
),
)
);
}
}
How can I fix it? Is there any ways? I'm debugging on my MI 8 SE phone
Try to change
Future<void> getLoc() async {
final position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
}
to
Future<void> getLoc() async {
final position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
this.test=position;
}