2

I have the following code that is inside a statefullWidget, and I want to change the statefullWidget to a statelessWidget and use GetXCintroller instead.

  AnimationController? _controller;
  Animation<Offset>? _slideAnimation;
  Animation<double>? _opacityAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(
        milliseconds: 300,
      ),
    );
    _slideAnimation = Tween<Offset>(
      begin: const Offset(0, -1.5),
      end: const Offset(0, 0),
    ).animate(
      CurvedAnimation(
        parent: _controller as Animation<double>,
        curve: Curves.fastOutSlowIn,
      ),
    );
    _opacityAnimation = Tween(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(
        parent: _controller as Animation<double>,
        curve: Curves.easeIn,
      ),
    );
    // _heightAnimation.addListener(() => setState(() {}));
  }

I tried to put it inside the onInit() method instead of initState() within a class extends GetxController with GetSingleTickerProviderStateMixin. It seems it works with no errors but when I launch the application on mobile platform, it closes the keyboard when I try to select the text fields on login/signup form. Actually this is a code that animates the login/signup form by switching the form and button text from login to signup mode. This is the form's code:

Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(10.0),
      ),
      elevation: 8.0,
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeIn,
        height: _authMode!.value == AuthMode.Signup ? 320 : 260,
        //height: _heightAnimation.value.height,
        constraints: BoxConstraints(
            minHeight: _authMode.value == AuthMode.Signup ? 320 : 260),
        width: deviceSize.width * 0.75,
        padding: const EdgeInsets.all(16.0),
        child: Form(
              key: _formKey,
              child: SingleChildScrollView(
                child: Column(
                  children: <Widget>[
                    TextFormField(
                      decoration: const InputDecoration(labelText: 'E-Mail'),
                      keyboardType: TextInputType.emailAddress,
                      validator: (value) {
                        if (value!.isEmpty || !value.contains('@')) {
                          return 'Invalid email!';
                        }
                      },
                      onSaved: (value) {
                        _authData['email'] = value as String;
                      },
                    ),
                    TextFormField(
                      decoration: const InputDecoration(labelText: 'Password'),
                      obscureText: true,
                      controller: _passwordController,
                      validator: (value) {
                        if (value!.isEmpty || value.length < 5) {
                          return 'Password is too short!';
                        }
                      },
                      onSaved: (value) {
                        _authData['password'] = value as String;
                      },
                    ),
                    AnimatedContainer(
                      constraints: BoxConstraints(
                        minHeight: _authMode.value == AuthMode.Signup ? 60 : 0,
                        maxHeight: _authMode.value == AuthMode.Signup ? 120 : 0,
                      ),
                      duration: const Duration(milliseconds: 300),
                      curve: Curves.easeIn,
                      child: FadeTransition(
                        opacity: _opacityAnimation as Animation<double>,
                        child: SlideTransition(
                          position: _slideAnimation as Animation<Offset>,
                          child: TextFormField(
                            enabled: _authMode.value == AuthMode.Signup,
                            decoration: const InputDecoration(
                                labelText: 'Confirm Password'),
                            obscureText: true,
                            validator: _authMode.value == AuthMode.Signup
                                ? (value) {
                                    if (value != _passwordController.text) {
                                      return 'Passwords do not match!';
                                    }
                                  }
                                : null,
                          ),
                        ),
                      ),
                    ),
                    const SizedBox(
                      height: 20,
                    ),
                    if (_isLoading!.value)
                      const CircularProgressIndicator()
                    else
                      Obx(() => ElevatedButton(
                            child: Text(_authMode.value == AuthMode.Login
                                ? 'LOGIN'
                                : 'SIGN UP'),
                            onPressed: _submit,
                            style: ElevatedButton.styleFrom(
                              shape: RoundedRectangleBorder(
                                borderRadius: BorderRadius.circular(30),
                              ),
                              primary: Theme.of(context).primaryColor,
                              padding: const EdgeInsets.symmetric(
                                  horizontal: 30.0, vertical: 8.0),
                              onPrimary: Theme.of(context)
                                  .primaryTextTheme
                                  .button!
                                  .color,
                            ),
                          )),
                    Obx(() => TextButton(
                          child: Text(
                              '${_authMode.value == AuthMode.Login ? 'SIGNUP' : 'LOGIN'} '),
                          onPressed: _switchAuthMode,
                          style: TextButton.styleFrom(
                            padding: const EdgeInsets.symmetric(
                                horizontal: 30.0, vertical: 4),
                            tapTargetSize: MaterialTapTargetSize.shrinkWrap,
                            textStyle: TextStyle(
                                color: Theme.of(context).primaryColor),
                          ),
                        )),
                  ],
                ),
              ),
            )),
      // ),
    );

The text button works(by wrapping it with Obx) and can change the form but text fields are un-clickable and the keyboard get closed immediately and form restarts again.

Hasani
  • 3,543
  • 14
  • 65
  • 125

1 Answers1

2

When you are using the GetX package, you have the option of creating a controller which extends GetxController.

Here is the example of the GetXController.

class AuthController extends GetxController{
  final key = GlobalKey<FormState>();
  late TextEditingController passwordController;
  @override
  void onInit() {
    super.onInit();
    passwordController = TextEditingController();
  }
  
  @override
  void onReady() {
    super.onReady();
  }
  
  @override
  void onClose() {
    super.onClose();
    passwordController.dispose();
  }
}

The alternative to initState() in GetX is onInit()

Please make sure to check this repo github.com/jabeed-ahmed/test.

If it solves your problem, kindly mark it as answered

Jabeed Ahmed
  • 125
  • 1
  • 4
  • 13
  • I did it as you can see in my other question here: https://stackoverflow.com/questions/70935600/is-it-possible-to-use-formkey-currentstate-inside-a-stateless-widget-with-getx?noredirect=1#comment125401590_70935600 But the problem is that I can not use `formKey.currentState` in this situation. – Hasani Feb 01 '22 at 05:58
  • would you like to connect? – Jabeed Ahmed Feb 01 '22 at 06:01
  • I uploaded the whole project and added the link in Edit part of the question. Please have a look. – Hasani Feb 01 '22 at 06:25
  • I have made some changes. It is working now. https://github.com/jabeed-ahmed/test. Please check here – Jabeed Ahmed Feb 01 '22 at 06:59
  • Thank you very much for your help but it seems the problem still exist. When I try to launch the application on Android emulator I can not type anything into the textFields. Also I uploaded a Deno backend named test2 on my github. It is the backend of this project that was working before I try to use GetX. But after that it doesn't work. It seems login/signup button in the flutter project can not send the information/request to the backend. – Hasani Feb 01 '22 at 16:48
  • I tried on real device. I was able to enter the data into textfields. Can you share the video? – Jabeed Ahmed Feb 02 '22 at 04:42
  • I don't know. I must find an screen recording app. Could you also check the app alongside with it's backend(test2 project on my Github). The test2 project doesn't need any installation and if you run it it will download automatically some libraries and works. The running command is also inside the README file of test2 project. I don't why my application can not work with the backend but it was working before converting to `GetX`. – Hasani Feb 03 '22 at 02:21
  • I believe you will have to raise the new question for the second. I hope the first one is solved. – Jabeed Ahmed Feb 03 '22 at 10:33
  • @JabeedAhmed what if I want to use the context in the onInit function, maybe for something like this ``` WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { BottomSheet.show(context: context); }); ``` . Is it possible? – Pratik Baid May 19 '22 at 09:25