I am new to flutter and I think I miss a little piece of information about constructor and stateful widget. I tried many ways but always have an error. I just want to pass data into my stateful widget to manipulate from there.
Here is my Error
The instance member 'widget' can't be accessed in an initializer.
Try replacing the reference to the instance member with a different expression
Here is my code
class CreateEducatorEventForm extends StatefulWidget {
final DateTime day = DateTime.now();
final String favoriteId = '';
CreateEducatorEventForm(DateTime day, String favoriteId);
#override
_CreateEducatorEventFormState createState() =>
_CreateEducatorEventFormState();
}
class _CreateEducatorEventFormState extends State<CreateEducatorEventForm> {
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
String _eventName = '';
String _eventDescription = '';
DateTime _eventDateStart = widget.day;
DateTime _eventDateFinish = widget.day;
You can just move it into initState
class _CreateEducatorEventFormState extends State<CreateEducatorEventForm> {
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
String _eventName = '';
String _eventDescription = '';
DateTime _eventDateStart;
DateTime _eventDateFinish;
#override
void initState() {
super.initState();
_eventDateStart = widget.day;
_eventDateFinish = widget.day;
}
}
To be fair, unless you really need to store this into your state (say, if it really participates in the lifecycle of your widget), you should just refer to it via widget.day whenever you need it.
Related
I am trying to clean up my code a lot. I have a form that I realise I use multiple times in my app. So I wanted to turn the form into its own stateful widget so I could easily re-use it. Unfortunately this is proving more difficult than I thought.
The form is first used in the app to add items. I then want to re-use that same form to edit items and change values in those text fields.
I call the new widget that I created:
body: const GearForm(
onSaveFunctionType: 'add',
itemID: 'none',
itemManufacturer: 'hi',
itemName: '',
itemType: 'test',
itemVolume: '',
itemWeight: '',
packCategory: '',
tempRating: '',
),
If I am passing arguments into the widget though they do not show up in the form when I am testing:
class GearForm extends StatefulWidget {
const GearForm(
{Key? key,
required this.onSaveFunctionType,
required this.itemID,
required this.itemName,
required this.itemManufacturer,
required this.itemType,
required this.packCategory,
required this.itemWeight,
required this.tempRating,
required this.itemVolume})
: super(key: key);
final String onSaveFunctionType;
final String itemID;
final String itemName;
final String itemManufacturer;
final String itemType;
final String packCategory;
final String itemWeight;
final String tempRating;
final String itemVolume;
#override
State<GearForm> createState() => _GearFormState();
}
//define category stream stream
Stream<DocumentSnapshot<Map<String, dynamic>>> streamUserCategories() {
FirebaseFirestore db = FirebaseFirestore.instance;
String userID = FirebaseAuth.instance.currentUser!.uid;
return db.collection('UserPackCategoryList').doc(userID).snapshots();
}
class _GearFormState extends State<GearForm> {
FirebaseAnalytics analytics = FirebaseAnalytics.instance;
FirebaseFirestore db = FirebaseFirestore.instance;
String userID = FirebaseAuth.instance.currentUser!.uid;
//text editing controllers
TextEditingController itemNameController = TextEditingController();
TextEditingController manufacturerController = TextEditingController();
TextEditingController itemTypeController = TextEditingController();
TextEditingController packCategoryController = TextEditingController();
TextEditingController itemWeightController = TextEditingController();
TextEditingController temperatureRatingController = TextEditingController();
TextEditingController volumeController = TextEditingController();
#override
Widget build(BuildContext context) {
//get entitlements from revenue cat
final Entitlement entitlement =
Provider.of<RevenueCatProvider>(context).entitlement;
print(widget.itemManufacturer);
//set initial controller values
itemNameController.text = widget.itemName;
manufacturerController.text = widget.itemManufacturer;
itemTypeController.text = widget.itemType;
packCategoryController.text = widget.packCategory;
itemWeightController.text = widget.itemWeight;
temperatureRatingController.text = widget.tempRating;
volumeController.text = widget.itemVolume;
The odd part is if I did a cmd+s in visual studio code then all the value would magically appear. Since the Cmd+s worked I thought it was showing the values on rebuild so I tried wrappign everythign in setState:
#override
Widget build(BuildContext context) {
//get entitlements from revenue cat
final Entitlement entitlement =
Provider.of<RevenueCatProvider>(context).entitlement;
print(widget.itemManufacturer);
setState(() {
//set initial controller values
itemNameController.text = widget.itemName;
manufacturerController.text = widget.itemManufacturer;
itemTypeController.text = widget.itemType;
packCategoryController.text = widget.packCategory;
itemWeightController.text = widget.itemWeight;
temperatureRatingController.text = widget.tempRating;
volumeController.text = widget.itemVolume;
});
but that didnt fix the issue either...
Update:
I did soem further troubleshooting and tried initState:
#override
void initState() {
super.initState();
print(widget.itemManufacturer);
itemNameController.text = widget.itemName;
manufacturerController.text = widget.itemManufacturer;
itemTypeController.text = widget.itemType;
packCategoryController.text = widget.packCategory;
itemWeightController.text = widget.itemWeight;
temperatureRatingController.text = widget.tempRating;
volumeController.text = widget.itemVolume;
//set initial controller values
}
What is super odd here is that my print(widget.itemManufacturer); works fine and I see the correct value. But it is not being assigned to the manufacturerController.text = widget.itemManufacturer; a few lines down.
Use initState or you can use late
late TextEditingController itemNameController =
TextEditingController.fromValue(TextEditingValue(text: widget.itemName));
The reason why the value were not showing in my form fields was because I am using autocomplete. Autocomplete upon further research has an initial Value field:
initialValue: TextEditingValue(text: manufacturerController.text),
Once I used that initalValue field all updated accordingly.
I'm trying to give default value to a TextFormField using text: parameter in it's controller
class _EditProfileState extends State<EditProfile> {
String firstName = "Sushant";
TextEditingController firstNameController = TextEditingController(text: firstName);
But am getting the following error
The instance member 'firstName' can't be accessed in an initializer.
Try replacing the reference to the instance member with a different expression
Try to use late with TextEditingController then initialize TextEditingController inside initState.
class _EditProfileState extends State<EditProfile> {
String firstName = "Sushant";
late TextEditingController firstNameController;
#override
void initState() {
firstNameController = TextEditingController(text: firstName);
}
}
#override
void dispose() {
firstNameController.dispose();
super.dispose();
}
Or use
class _EditProfileState extends State<EditProfile> {
String firstName = "Sushant";
TextEditingController firstNameController = TextEditingController();
#override
void initState() {
firstNameController.text = firstName;
}
}
#override
void dispose() {
firstNameController.dispose();
super.dispose();
}
Here you are trying to initiate firstNameController with a non-constant value. firstName here is not static and belongs to the instance.
When the class is created, it cannot access properties of itself because they are not created either (nothing guarantees that firstName is created before firstNameController).
However, you can use the late keyword:
class _EditProfileState extends State<EditProfile> {
String firstName = "Sushant";
late TextEditingController firstNameController = TextEditingController(text: firstName);
}
late will make firstNameController "lazy". It means it is only going to be evaluated when called (and not when your class instance is created). Therefore, when using the late keyword, when evaluated, your class is already created with its properties and you can access them.
Since you are using State, set default text in initState.
This will only trigger once when Widget is initialized. Also set all TextEditingController as final, you should not assign new one later.
Last, remember to dispose it.
String firstName = "Sushant";
final TextEditingController firstNameController = TextEditingController();
#override
void initState() {
firstNameController.text = firstName;
super.initState();
}
#override
void dispose() {
firstNameController.dispose();
super.dispose();
}
I Can't Change the state of any variable with .obs, RxType and Rx();
And when i try to change it it will give errors that ican't assign a value type int to variable type RxInt.
Here is the code:
class StateController extends GetxController {
late String sunrise;
late String sunset;
late int temperatureDegree;
late int maxDegree;
late int minDegree;
late double windSpeed;
late int humidity;
void updateUI(dynamic weatherDataInput) {
sunrise = weatherDataInput["current"]["sunrise"];
sunset = weatherDataInput["current"]["sunset"];
temperatureDegree = weatherDataInput["current"]["temp"];
maxDegree = weatherDataInput["daily"][0]["temp"]["max"];
minDegree = weatherDataInput["daily"][0]["temp"]["min"];
windSpeed = weatherDataInput["current"]["wind_speed"];
humidity = weatherDataInput["current"]["humidity"];
}
}
try this way,
class NameController extends GetxController{
final sunrise = ''.obs;
void updateSomeText(){
sunrise('Text updated'); //or sunrise(weatherDataInput["current"]
//["sunrise"].toString());
}
}
then to update it try wrap it with Obx e.g.:
final controller = Get.put(NameController());
Obx(
()=> Text(controller.sunrise.value)
),
You can use the update() method in the end of the updateUI() like this:
void updateUI(dynamic weatherDataInput) {
sunrise = weatherDataInput["current"]["sunrise"];
sunset = weatherDataInput["current"]["sunset"];
temperatureDegree = weatherDataInput["current"]["temp"];
maxDegree = weatherDataInput["daily"][0]["temp"]["max"];
minDegree = weatherDataInput["daily"][0]["temp"]["min"];
windSpeed = weatherDataInput["current"]["wind_speed"];
humidity = weatherDataInput["current"]["humidity"];
update();
}
,
and then use GetBuilder in your UI, alternatively, you should declare your variables as Rx, for example:
RxString sunrise = "".obs;
RxString sunset = "".obs;
and use observer widget in your UI:
Obx(
()=> Text(controller.sunset.value)
)
This will update your UI automatically when observables (sunrise and sunset) change.
.
I'm bit new in flutter. And I'm having little error on this matter.
I have here a code snippet of where I passed lat and long data from another screen and I want to give acccess on MapsState. But I'm getting a property error The instance member 'widget' can't be accessed in an initializer.
class Maps extends StatefulWidget {
Maps({this.lati, this.longi}) : super();
final double lati;
final double longi;
final String title = "Select Location";
#override
MapsState createState() => MapsState ();
}
class MapsState extends State<Maps> {
//
Completer<GoogleMapController> _controller = Completer();
LatLng _center = LatLng(widget.lati, widget.longi);
final Set<Marker> _markers = {};
LatLng _lastMapPosition = _center;
MapType _currentMapType = MapType.normal;
Please help.
You can't access your variables when defining another variable. I suggest you to define them but give them values in initState method:
LatLng _center;
#override
void initState(){
super.initState();
_center = LatLng(widget.lati, widget.longi);
}
The following code is part of my login screen. I am using shared_preferences to save the remember me checkbox and the user name. The checkbox value is working, but the user name is not being set. I know it is being saved because I can see the value using print. But I guess it is being set too late, as my TextField is blank. Any ideas?
class RmoLogin extends StatefulWidget {
static const String id = 'login_screen';
#override
_RmoLoginState createState() => _RmoLoginState();
}
class _RmoLoginState extends State<RmoLogin> {
final TextStyle style = TextStyle(fontFamily: 'Montserrat', fontSize: 20.0);
final TextEditingController usernameController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
bool showSpinner = false;
bool rememberMe = false;
String userName = '';
_saveRememberUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('username', usernameController.text);
prefs.setBool('remember', rememberMe);
}
_getRememberUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
userName = prefs.getString('username') ?? '';
rememberMe = prefs.getBool('remember') ?? false;
}
#override
void initState() {
super.initState();
_getRememberUser();
if (rememberMe) {
usernameController.text = userName;
}
}
#override
Widget build(BuildContext context) {
final userField = UserTextField(style: style, usernameController: usernameController);
final passwordField = PasswordTextField(style: style, passwordController: passwordController);
final rememberMeCheckbox = Checkbox(
value: rememberMe,
onChanged: (newValue) {
setState(() {
rememberMe = newValue;
});
},
);
Because the SharedPreferences instantiation is asynchronous and the build method itself is not, by the time the instance is created and the value is accessed, the build method would have already been called and your widgets built.
However, For things like SharedPreferences and databases, the best way to implement them is to use the Singleton approach, i.e, one instance that you can use across the entire application.
In this case instead of creating a new SharedPreferences instance whenever either method is called, it will be best to create a single instance of it and use that instance to update the set and get the values.
You could also create a service for the SharedPreferences which you could use across the entire application.
class SharedPreferencesService {
final SharedPreferences _prefs;
SharedPreferencesService(this._prefs);
// define methods here
}
Then in your main method, you can create an instance of SharedPreferences and use it to initialize the SharedPreferencesService.
eg.
SharedPreferencesService service;
Future<void> main() async {
// flutter will complain if this isn't present
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
service = SharedPreferencesService(prefs);
runApp(YourApp());
}
Then wherever you want to use it in your app, you can call the method name on the service. It is best to use Dependency Injection for the SharedPreferencesService though, you can try the get_it library.
Just a brief look.... but
You are calling _getRememberUser synchronously within initState() when it is an async method
initState() isn't the correct place to be calling it as initState() itself is a synchronus #override.
My solution would be to use a FutureBuilder in your build() method, and call _getRememberUser there.
You just need to update the state that's it check the below solution:
bool rememberMe = false;
String userName = '';
#override
void initState() {
// TODO: implement initState
super.initState();
_getRememberUser();
}
_getRememberUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
userName = prefs.getString('username') ?? '';
rememberMe = prefs.getBool('remember')?? false;
});
if (rememberMe) {
usernameController.text = userName;
}
print(userName +'-->'+rememberMe.toString());
}
Happy Coding!!