Home > Back-end >  passing a widget that has setState to another page without stateful/stateless widget
passing a widget that has setState to another page without stateful/stateless widget

Time:01-30

Are there any way on how to pass a widget function to another page that is without any stateless/stateful? The file only includes widgets such as textfields, buttons and etc. I am trying not to cluster every fields in one page. Any helps/ideas would be appreciated!

Main.dart

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  @override
  State<Main Page> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
// bool for toggling password
  bool isSecuredPasswordField = true;
  @override
  Widget build(BuildContext context) {
    return Container();
  }
// widget function that I need to pass on widget_fields.dart
 Widget togglePassword() {
    return IconButton(
      onPressed: () {
        setState(() {
          isSecuredPasswordField = !isSecuredPasswordField;
        });
      },
      icon: isSecuredPasswordField
          ? const Icon(Icons.visibility)
          : const Icon(Icons.visibility_off),
    );
  }
}

widget_fields.dart

Widget userPasswordField(_passwordUserCtrlr) {
  return Padding(
    padding: const EdgeInsets.symmetric(horizontal: 25.0),
    child: TextFormField(
      obscureText: true,
      controller: _passwordUserCtrlr,
      keyboardType: TextInputType.visiblePassword,
      decoration: InputDecoration(
        isDense: true,
        suffixIcon: togglePassword(), //<-- I wanna call that function here
        prefixIcon: const Icon(Icons.lock),
        enabledBorder: OutlineInputBorder(
          borderSide: const BorderSide(color: Color(0xFFCECECE)),
          borderRadius: BorderRadius.circular(12),
        ),
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(12),
          borderSide: const BorderSide(color: Color(0xFFCECECE)),
        ),
        hintText: 'Password',
        hintStyle: const TextStyle(
          fontFamily: 'Poppins',
          fontSize: 14,
        ),
        fillColor: const Color(0xFFFEFEFE),
        filled: true,
      ),
      validator: (value) {
        if (value!.isEmpty) {
          return "Please enter your password.";
        } else if (value.length < 8) {
          return "Password should be min. 8 characters.";
        } else {
          return null;
        }
      },
    ),
  );
}

CodePudding user response:

This is Example that how you call Widget in another class:

  class MainApge extends StatefulWidget {
  const MainApge({Key? key}) : super(key: key);

  @override
  State<MainApge> createState() => _MainApgeState();
}

class _MainApgeState extends State<MainApge> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ContainerTextFields.customsTextField(
          "User Name",
          'enter name',
          userNameController,
        ),
      ],
    );
  }
}

This is Custom Widget Class:

 class ContainerTextFields {
      static Widget customsTextField(
          String label, String cusHintText, TextEditingController _controller) {
        return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
          Padding(
              padding: EdgeInsets.only(
                  left: SizeConfig.screenHeight! * 0.05,
                  top: SizeConfig.screenHeight! * 0.03),
              child: Text(
                label,
                style: AppStyle.kUnSyncedDialogeText.copyWith(
                    color: AppColors.kTextFieldLabelColorGrey,
                    fontWeight: FontWeight.bold),
              )),
          Padding(
            padding: EdgeInsets.only(
                top: SizeConfig.screenHeight! * 0.02,
                left: SizeConfig.screenHeight! * 0.042,
                right: SizeConfig.screenWidth! * 0.042),
            child: SingleChildScrollView(
              child: Container(
                height: SizeConfig.screenHeight! * 0.065,
                child: TextFormField(
                  controller: _controller,
                  decoration: InputDecoration(
                    hintText: cusHintText,
                    hintStyle: TextStyle(
                      color: AppColors.kLoginPopUpColor,
                    ),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                  ),
                ),
              ),
            ),
          )
        ]);
      }
    }

CodePudding user response:

You can pass functions like any other variable. I made a full working example that's different than yours to show a more minimal example but you can apply the same logic for your code

main.dart

import 'package:flutter/material.dart';

import 'column.dart';

void main() {
  runApp(const MaterialApp(home: MyApp()));
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  Widget returnSomeText() {
    return const Text("test");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: createColumn(returnSomeText));
  }
}

column.dart

import 'package:flutter/material.dart';

Widget createColumn(Function widgetFunction) {
  return Column(
    children: [widgetFunction(), widgetFunction()],
  );
}

As you can see the togglePassword from your code corresponds to returnSomeText in mine. and userPasswordField is like createColumn. But it must be said that it's not recommended to use helper functions like createColumn here but to turn it into a StatelessWidget, like this for example:

import 'package:flutter/material.dart';

class CreateColumn extends StatelessWidget {
  final Function widgetFunction;
  const CreateColumn({Key? key, required this.widgetFunction}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [widgetFunction(), widgetFunction()],
    );
  }
}

And then in main.dart:

return Scaffold(body: CreateColumn(widgetFunction: returnSomeText));

See also this YouTube video: Widgets vs helper methods

CodePudding user response:

You can pass the widget as parameter to child widget:

class MyTextField extends StatelessWidget {
  const MyTextField({Key? key,
    this.togglePassword,
    this.passwordUserCtrlr
  })
      : super(key: key);

  final Widget? togglePassword;
  final TextEditingController? passwordUserCtrlr;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 25.0),
      child: TextFormField(
        obscureText: true,
        controller: passwordUserCtrlr,
        keyboardType: TextInputType.visiblePassword,
        decoration: InputDecoration(
          isDense: true,
          suffixIcon: togglePassword, //<-- I wanna call that function here
          prefixIcon: const Icon(Icons.lock),
          enabledBorder: OutlineInputBorder(
            borderSide: const BorderSide(color: Color(0xFFCECECE)),
            borderRadius: BorderRadius.circular(12),
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: const BorderSide(color: Color(0xFFCECECE)),
          ),
          hintText: 'Password',
          hintStyle: const TextStyle(
            fontFamily: 'Poppins',
            fontSize: 14,
          ),
          fillColor: const Color(0xFFFEFEFE),
          filled: true,
        ),
        validator: (value) {
          if (value!.isEmpty) {
            return "Please enter your password.";
          } else if (value.length < 8) {
            return "Password should be min. 8 characters.";
          } else {
            return null;
          }
        },
      ),
    );
  }
}

And can easily call from main widget:

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
// bool for toggling password
  bool isSecuredPasswordField = true;
  TextEditingController? passwordUserCtrlr = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return MyTextField(
      togglePassword: togglePassword(),
      passwordUserCtrlr: passwordUserCtrlr,
    );
  }
// widget function that I need to pass on widget_fields.dart
  Widget togglePassword() {
    return IconButton(
      onPressed: () {
        setState(() {
          isSecuredPasswordField = !isSecuredPasswordField;
        });
      },
      icon: isSecuredPasswordField
          ? const Icon(Icons.visibility)
          : const Icon(Icons.visibility_off),
    );
  }
}

CodePudding user response:

You can create class like GlobalWidget for example, like this:

class GlobalWidget {

 // widget function that I need to pass on widget_fields.dart
 Widget togglePassword(Function()? onPressed, bool value) {
    return IconButton(
      onPressed: onPressed,
      icon: value
          ? const Icon(Icons.visibility)
          : const Icon(Icons.visibility_off),
    );
  }

}

And You can call the Widget like that :

GlobalWidget().togglePassword(() => setState(() {
    isSecuredPasswordField = !isSecuredPasswordField;
  }), isSecuredPasswordField)

CodePudding user response:

What you are trying to do is impossible in the Flutter framework. You cannot call methods belonging to other widgets

Also, it is discouraged to use function to return widgets as this impacts the framework's ability to optimize the build process.

One possible solution is to package your complete password entry in a set of custom (statefull) widgets. You can collect those into a single source file if you like. Be sure to create a class for every widget.

  • Related