2

I am trying to convert a code that uses provider package and stateful widget to use GetX package and stateless widget. One problem I do have is that I have a form that changes(from login to signup and vice versa) using animation, but when I check the result of the following line if (!_formKey.currentState!.validate()) it seems it doesn't work.

This is the related codes:

auth_controller.dart:

enum AuthMode { Signup, Login }

class AuthController extends GetxController with GetSingleTickerProviderStateMixin  {
  static AuthController instance = Get.find();
  Rx<dynamic>? authMode = AuthMode.Login.obs;
  RxBool? isLoading = false.obs;
  String? _token; 
  DateTime? _expiryDate;
  String? _userId;
  Timer? _authTimer;
final _isAuth = false.obs;


  AnimationController? controller;
  Animation<Offset>? slideAnimation;
  Animation<double>? opacityAnimation;

    @override
  void onInit() {
    super.onInit();
    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(() {}));
  }
.
.
.

auth_screen.dart:

return 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: Obx(() => 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
                      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,
                            ),
                          ),
                     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),
                          ),
                        ),
                  ],
                ),
              ),
            )),
      ),
    );

I don't know what is the problem and how can I convert the code in a right way? I also don't know if is it possible at all or not?

Hasani
  • 3,543
  • 14
  • 65
  • 125
  • Can you tell me where exactly are you planning to have it here? – Jabeed Ahmed Feb 01 '22 at 05:39
  • @JabeedAhmed: Sorry couldn't understand. May you explain more? – Hasani Feb 01 '22 at 05:54
  • @JabeedAhmed: As you can see above, I put some of the animation codes inside the `onInit()` within the `GetXController` class. But when I want to see the state of form which uses this animation, it doesn't work. `if (!_formKey.currentState!.validate())` doesn't work. – Hasani Feb 01 '22 at 06:01

1 Answers1

1

Yes, you can use FormState with GetX controllers and a stateless widget, however you need to instantiate the GlobalKey in your controller and then ensure the controller is "Put" on the UI for use.

class YourController extends GetXController {
  final formKey = GlobalKey<FormState>();
}

Then inject the controller as a dependency in your stateless widget

class YourWidget extends StatelessWidget {
   final getxController = Get.put(YourController());
}

Then add it to your Form Widget

    child: Form(
      key: getxController.formKey,

Once you have added the required validation functions to your controller and TextInput widgets you can call formKey.Validate() from within your Getx extended controller class

 void submitForm() {
    final isValid = formKey.currentState.validate();
}
Yonkee
  • 1,781
  • 3
  • 31
  • 56