Flutter GetX state management can't update the variables state - flutter

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.
.

Related

Read provider inside a global or static method in Flutter

I have a question, regarding reading providers from inside static methods or global methods. I am using riverpod and awesome_notification packages, and I need to alter the state the app, from the action of the notification, for this, the package uses static methods inside a controller class.
class NotificationController{
...
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction) async {
...//some way to access a provider, to call methods on it
}
...
}
If there is another way of doing this that I am not seeing, please let me know.
I have not been able to find a way to do this.
You can:
Pass to the ref function as a parameter.
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction, Ref ref) async {
final some = ref.read(someProvider);
}
Create a class that accepts the ref field in the constructor.
final notificationProvider = Provider((ref) => NotificationController(ref));
// or use tear-off
final notificationProvider = Provider(NotificationController.new);
class NotificationController {
NotificationController(Ref ref) {
_ref = ref;
}
static late final Ref _ref;
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction) async {
final some = _ref.read(someProvider);
}
}
An additional example:
import 'package:riverpod/riverpod.dart';
final valueProvider = Provider<int>((_) => 5);
final managerProvider = Provider(ManagerProvider.new);
class ManagerProvider {
ManagerProvider(Ref ref) {
_ref = ref;
}
static late final Ref _ref;
static int getValue() => _ref.read(valueProvider);
}
void main() {
final container = ProviderContainer();
container.read(managerProvider);
final value = ManagerProvider.getValue();
print(value); // 5
}
Either way, you should always have access to `Ref'.
Update:
As #OppositeDragon and #Eran Ravid pointed out, we really can't access _ref in a static method. However, if you define _ref in the constructor, it is possible. I think it's a terrible anti-pattern, though. Use method 1 and you will be fine.

LateInitializationError: Field 'day0' has not been initialized

import 'package:intl/intl.dart';
late String day0;
late String dateMonth0;
late String day1;
late String dateMonth1;
late String day2;
late String dateMonth2;
late String day3;
late String dateMonth3;
late String day4;
late String dateMonth4;
late String day5;
late String dateMonth5;
void findDateTime(DateTime time) {
day0 = DateFormat('EEEEE', 'en_US').format(time);
dateMonth0 = DateFormat('d MMMM', 'en_US').format(time);
DateTime dateTime1 = DateTime(time.year, time.month, time.day + 1);
day1 = DateFormat('EEEEE', 'en_US').format(dateTime1);
dateMonth1 = DateFormat('d MMMM', 'en_US').format(dateTime1);
DateTime dateTime2 = DateTime(time.year, time.month, time.day + 2);
day2 = DateFormat('EEEEE', 'en_US').format(dateTime2);
dateMonth2 = DateFormat('d MMMM', 'en_US').format(dateTime2);
DateTime dateTime3 = DateTime(time.year, time.month, time.day + 3);
day3 = DateFormat('EEEEE', 'en_US').format(dateTime3);
dateMonth3 = DateFormat('d MMMM', 'en_US').format(dateTime3);
DateTime dateTime4 = DateTime(time.year, time.month, time.day + 4);
day4 = DateFormat('EEEEE', 'en_US').format(dateTime4);
dateMonth4 = DateFormat('d MMMM', 'en_US').format(dateTime4);
DateTime dateTime5 = DateTime(time.year, time.month, time.day + 5);
day5 = DateFormat('EEEEE', 'en_US').format(dateTime5);
dateMonth5 = DateFormat('d MMMM', 'en_US').format(dateTime5);
}
above code is to fetch day date and month of upcoming 6 days using current date
i have created pickup and delivery page in which i want to show these dates.I have called the above function find date time in the init state of PickupScreen and the code is attached below but i am still getting late initialisation error on all the variables declared late any way i could solve this?
class PickupScreen extends ConsumerStatefulWidget {
const PickupScreen({Key? key}) : super(key: key);
#override
ConsumerState<ConsumerStatefulWidget> createState() => _PickupScreenState();
}
class _PickupScreenState extends ConsumerState<PickupScreen> {
#override
void initState() {
super.initState();
time();
}
void time() async {
DateTime time = await Time().getTime();
findDateTime(time);
}
Check this
Your concept added to a loop and it will help to get the values with the help of index. you can call the static function from your code and await for it to add the results to the list
void time() async {
This function is asyncronous. That means it will run and return a Future of some kind to notify you when it is done. Since yours does not return a future (why is your linter not flagging this? Did you turn it off?) you are unable to determine when it is done.
So when you call it:
void initState() {
super.initState();
time();
}
It will return immediately. The point is that the Future it returns can be used with the await keyword to make sure it is finished. But you skipped that by chosing to return void.
So it will return immediately, but unlike non-async function, it will not actually be done. So your initState completes and the build starts, but your variables declared as late aren't actually assigned yet. That is why you get the exception.
There is no easy fix here. You have to understand how Futures and async/await patterns work and then find the best solution to your problem.
A good start might be What is a Future and how do I use it?

GetxController structure

class Controller extends GetxController {
int currentIdx = ''.obs;
void updateInt(int idx) {
currentIdx = idx.obs;
}
}
here is my controller. Now I put this line a widget to get idx value and assign immediately to currentIdx value:
Controller().updateInt(idx);
but .obs lines gives error in this current structure.
when I change the controller:
class Controller extends GetxController {
RxInt currentIdx = 0.obs;
void updateInt(RxInt idx) {
currentIdx = idx;
}
}
this time Controller().updateInt(idx); gives error. (RxInt not compatible int value)
how can I handle this?
You need to declare and use like that:
RxInt currentIdx = 0.obs;
void updateInt(int id) {
currentIdx.value = id;
}

Flutter Riverpod StateNotifierProvider: only watch changes for part of a model

I have a model like this:
class TimerModel {
const TimerModel(this.timeLeft, this.buttonState);
final String timeLeft;
final ButtonState buttonState;
}
enum ButtonState {
initial,
started,
paused,
finished,
}
And here is the StateNotifierProvider:
class TimerNotifier extends StateNotifier<TimerModel> {
TimerNotifier() : super(_initialState);
static const int _initialDuration = 10;
static final _initialState = TimerModel(
_durationString(_initialDuration),
ButtonState.initial,
);
final Ticker _ticker = Ticker();
StreamSubscription<int> _tickerSubscription;
void start() {
if (state.buttonState == ButtonState.paused) {
_tickerSubscription?.resume();
state = TimerModel(state.timeLeft, ButtonState.started);
} else {
_tickerSubscription?.cancel();
_tickerSubscription =
_ticker.tick(ticks: _initialDuration).listen((duration) {
state = TimerModel(_durationString(duration), ButtonState.started);
});
_tickerSubscription.onDone(() {
state = TimerModel(state.timeLeft, ButtonState.finished);
});
state =
TimerModel(_durationString(_initialDuration), ButtonState.started);
}
}
static String _durationString(int duration) {
final String minutesStr =
((duration / 60) % 60).floor().toString().padLeft(2, '0');
final String secondsStr =
(duration % 60).floor().toString().padLeft(2, '0');
return '$minutesStr:$secondsStr';
}
void pause() {
_tickerSubscription?.pause();
state = TimerModel(state.timeLeft, ButtonState.paused);
}
void reset() {
_tickerSubscription?.cancel();
state = _initialState;
}
#override
void dispose() {
_tickerSubscription?.cancel();
super.dispose();
}
}
class Ticker {
Stream<int> tick({int ticks}) {
return Stream.periodic(Duration(seconds: 1), (x) => ticks - x - 1)
.take(ticks);
}
}
I can listen for all changes in state like this:
final timerProvider = StateNotifierProvider<TimerNotifier>((ref) => TimerNotifier());
However I want to make another provider that only listens for changes in the ButtonState. This doesn't work:
final buttonProvider = StateProvider<ButtonState>((ref) {
return ref.watch(timerProvider.state).buttonState;
});
because it still returns all the state changes.
This also doesn't work:
final buttonProvider = StateProvider<ButtonState>((ref) {
return ref.watch(timerProvider.state.buttonState);
});
Because the state object doesn't have a buttonState property.
How do I only watch buttonState changes?
Using watch gives a new state whenever the watched state changes. So can solve the problem in two parts like so:
final _buttonState = Provider<ButtonState>((ref) {
return ref.watch(timerProvider.state).buttonState;
});
Using this provider will cause a rebuild every time the timerProvider.state changes. However, the trick is to do the following:
final buttonProvider = Provider<ButtonState>((ref) {
return ref.watch(_buttonState);
});
Since _buttonState will be the same for most of the timerProvider.state changes, watching _buttonState will only cause rebuilds when _buttonState actually changes.
Thanks to this post for showing the answer. That post also indicates that there will be a simplified syntax soon:
final buttonState = ref.watch(timerProvider.state.select((state) => state.buttonState));

How to pass data in a stateful widget with constructor?

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.