Flutter getx controller get the current page context - flutter

I would like to use context to show a custom dialog from cool alert in getxcontroller method.
I have created the following controller
class HomePageController extends GetxController {
#override
void onInit() {
super.onInit();
getData();
}
void getData(){
//perform http request here
//show cool alert
CoolAlert.show(
context: context, //here needs the build context
type: CoolAlertType.success
);
}
}
Am using this controller in my stateless widget like
class HomePage extends StatelessWidget {
HomePage({ Key? key }) : super(key: key);
final _c = Get.find<HomePageController>();
#override
Widget build(BuildContext context) {
return Container(
);
}
}
How can i get the current homepage BuildContext in the controller inorder to show the cool alert.

If you want to show a dialog or snackbar what need context as a required agument. You can use Get.dialog() and Get.snackbar, there function work same as showDialog and showSnackbar but *without* context or scaffod

you can add the context to the construct function:
#override
Widget build(BuildContext context) {
Get.put(HomePageController(context: context));
return Container();
}
and for the HomePageController:
note: you need to wrap the function with Future.delayed(Duration.zero)
otherwise it will throw an error
class HomePageController extends GetxController {
late BuildContext context;
HomePageController({required this.context});
void getData(){
Future.delayed(Duration.zero,(){
CoolAlert.show(
context: context, //here needs the build context
type: CoolAlertType.success
);
});
}
...
}

You Need to Initialize the controller on the homepage like following
class HomePage extends StatelessWidget {
HomePage({ Key? key }) : super(key: key);
final _c = Get.put(HomePageController())..getData(context);
#override
Widget build(BuildContext context) {
return Container(
);
}
}
This will call getData Function and remove the onInit Function and Pass Buildcontext context parameter in getData Function.

Related

Update TextEditingController Text with Riverpod

I'm new to Riverpod and am trying to migrate an app over from Provider. If I had a TextField and wanted to set its value based on my Provider model, I would do this:
class MyWidget extends StatefulWidget{
const MyWidget({ Key? key }) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
var controller = TextEditingController();
#override
void didChangeDependencies() {
super.didChangeDependencies();
//Set the value here...
var model = Provider.of<Model>(context);
controller.text = model.name;
}
Widget build(BuildContext context) {
return TextField(controller: controller)
}
}
As I understand it, didChangeDependencies() would listen to changes from Provider.of<Model>(context) and update my controller accordingly.
I'm trying to pull off the same thing with Provider, but I can't ever get the TextField's value to show up.
class MyWidget extends ConsumerStatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
ConsumerState<ConsumerStatefulWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends ConsumerState<MyWidget> {
var controller = TextEditingController();
#override
void didChangeDependencies() {
super.didChangeDependencies();
//Trying the same thing here...
final name = ref.watch(providerName);
controller.text = name;
}
Widget build(BuildContext context) {
final name = ref.watch(providerName);
return Column(
children: [
//This doesn't work:
TextField(controller: controller),
//I know my provider has the value, because this works fine:
Text(name),
]
}
}
How can I get my TextEditingController's text property to update?
From Riverpod official website
///1.Create a [StateNotifier] sub-class, StateNotifier is something where you can define functions that can change your state like in this state is of String type, you also can use objects (Classes instead of primitive types)
class Counter extends StateNotifier<String> {
Counter() : super('');
void changeText(String text){
state=text;
}
///2.Create a provider [StateNotifierProvider] with this you can use in your widget
final counterProvider = StateNotifierProvider<Counter, String>((ref) {
return Counter();
});
///3.Consume the Provider this is how we can attach state with our widget
class Home extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
final text = ref.watch(counterProvider);
return Text('$text');
}
}
so here you can add you widget like button and onTap executes the code like
onTap()=>changeText(textController.text);
So your text [Text('$text');] will automatically change.
String inputText = controller.text;

Flutter Reactive State

I define a model reactively with GetX and send this model reactively to the view with the help of StateMixin. But this variable I sent changes the main variable as well. How exactly does this happen and how can I fix it? In the example I gave below, when I change the id value, the id automatically changes in the rawMyModel model. But I don't want it to change.
detail_controller.dart
class DetailController extends GetxController with StateMixin<Rx<MyModel>> {
late final MyModel rawMyModel;
#override
void onInit() async {
super.onInit();
rawMyModel = (Get.arguments as MyModel);
change(Rx(rawMyModel), status: RxStatus.success());
}
void reset() {
change(Rx(rawMyModel), status: RxStatus.success());
}
}
detail_page.dart
class DetailPage extends GetView<DetailController> {
const DetailPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: controller.obx((Rx<MyModel>? myModel) => _buildBody(myModel: myModel!)),
);
}
Widget _buildBody({required Rx<MyModel> myModel}) {
print(myModel.value.toString());
myModel.update((val) => val.id = 5); // change
}
}

How do I access an variable from a StatefulWidget inside an StatelessWidget?

How do I access the variable "selectedTag" from this statefulWidget:
class _AlertDialogOneState extends State<AlertDialogOne> {
Item selectedTag;
...
}
}
inside this statelessWidget :
class CardTile extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(...
Pass it as parameter,
class CardTile extends StatelessWidget {
final Item selectedTag;// Add this
CardTile(this.selectedTag); // Add this
#override
Widget build(BuildContext context) {
return Container(...
To pass this variable, you have multiple ways:
Pass it as a constructor when u navigate to this class using your navigator
Navigator.push(
context,
MaterialPageRoute(builder: (context) => CardTile(selectedTag)),
);
class CardTile extends StatelessWidget {
Item selectedTag;
CardTile(this.selectedTag);
#override
Widget build(BuildContext context) {
return Container(...
Use a state management like provider
class ProviderData with ChangeNotifier {
Item selected;
void changeSelection(newSelect) {
selected = newSelect;
changeNotifier();
}
}
and inside any class you need call this:
final providerData = Provider.of<ProviderData>(context);
so you can access the variable or change it using this instance like this:
final variable = providerData.selected;
providerData.changeSelection(newValue);
print(variable);
hope this help but i see that it is better to pass it through the constructor if you are not using a state managemnt, however i just gave you an example for illustration

How do I have my app change screens based on my user state?

I am using the ScopedModel pattern, but I am also interested how this same problem is addressed in the similar Provider pattern.
Currently I have a ScopedModel with a bool exposed called loggedIn. When the FirebaseonAuthStateChanged stream changes user log in state, my ScopedModel changes that bool, and calls NotifyListeners. All straight forward stuff.
Now I am confused as to the best way to push or pop routes based on this ScopedModel.
Should all my logged in screens (screens that require a user) have the following code in build method?
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!auth.hasUser)
Navigator.of(context).pushNamedAndRemoveUntil('/entry', (Route<dynamic> route) => });
});
That seems a little excessive to have this code on every single screen. Is there a way I can define this log screen change behaviour somewhere only once?
create a Widget for it ;)
class Validation extends StatefulWidget {
final Function validator;
final Widget child;
const Validation({Key key, this.validator, this.child}) : super(key: key);
#override
_ValidationState createState() => _ValidationState();
}
class _ValidationState extends State<Validation> {
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
widget.validator();
});
super.initState();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
now use it everywhere
#override
Widget build(BuildContext context) {
return Validation(
validator: (){
if (!auth.hasUser){
Navigator.of(context).pushNamedAndRemoveUntil('/entry', (Route<dynamic> route) => false);
}
},
child: MyAwesomePage(),
);
}
you can further simplify if the validation is same everywhere or create multiple validation widget according to the validations required,
FOR YOUR CASE
class LoginValidation extends StatefulWidget {
final String routeIfNotLoggedIn;
final Widget child;
const LoginValidation({Key key, this.routeIfNotLoggedIn, this.child}) : super(key: key);
#override
_LoginValidationState createState() => _LoginValidationState();
}
class _LoginValidationState extends State<LoginValidation> {
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (!auth.hasUser){
Navigator.of(context).pushNamedAndRemoveUntil(widget.routeIfNotLoggedIn, (Route<dynamic> route) => false);
}
});
super.initState();
}
}
and use it
#override
Widget build(BuildContext context) {
return LoginValidation(
routeIfNotLoggedIn: "/myLoginRoute",
child: MyAwesomePage(),
);
}

use passed data before build method flutter

I'm trying to access the information I've passed over from a previous class before the build method begins. But it's saying only static members can be accessed in initializers. I don't really want to use the static property, partially because I wouldn't know how to use it, but also because I think it seems unnecessary. In previous pages I've been able to access the data but only after the build method, does anyone know how I can access it before? Thanks all
class FirstPage extends StatefulWidget {
#override
_FirstPageState createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
List<MyProvider> myList;
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: myList.length,
itemBuilder: (context, index) {
String imgPath = myList[index].image;
String myTextPath = myList[index].name;
String locationNamePath = myList[index].location;
double distancePath = myList[index].distance;
String myName = '${myTextPath} ''${locationNamePath}';
return MyCard(
locationText: locationNamePath,
myText: myTextPath,
assetImage: Image.network(imgPath),
function: (){
Provider.of<Data>(context, listen: false).addLogo(Image.network(imgPath));
Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage(myName: myName,)));
},
);
}),
);
}
}
My next page accesses the data using a key but it seems not to be able to use it before the build method, and that's what I need to get around!
class SecondPage extends StatefulWidget {
final String myName;
const SecondPage({Key key, this.myName})
: super(key: key);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> with TickerProviderStateMixin {
final CollectionReference myItemsReference = Firestore.instance.collection('${widget.myName}');
// This is where the error is
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
Use the initState method for anything related to initialization of State. See this for more on initState.
Example:
CollectionReference myItemsReference;
#override
void initState() {
myItemsReference = Firestore.instance.collection('${widget.myName}');
}