Abdus Sattar Bhuiyan
Abdus Sattar Bhuiyan

Reputation: 3074

How to extract TextFormField as reusable widget in flutter

I want to make my flutter project highly manageable, apply clean code and maintain DRY concept strictly. There are a lot of input elements in any flutter project. So I want to make this element as a separate widget so that if I want to change in future then I will change in one place. Here is my approach:

import 'package:flutter/material.dart';

import '../utility/validatation.dart';

class RegistrationPage extends StatefulWidget {
  static const String routeName = '/registrationPage';

  @override
  State<RegistrationPage> createState() => _RegistrationPageState();
}

class _RegistrationPageState extends State<RegistrationPage> {
  final _formKey = GlobalKey<FormState>();
  final TextEditingController nameInput = TextEditingController();
  final TextEditingController businessName = TextEditingController();
  final TextEditingController productTypeId = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: new Form(
        key: _formKey,
        autovalidateMode: AutovalidateMode.onUserInteraction,
        child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
          Container(
            height: 70,
            margin: EdgeInsets.only(bottom: 50),
            child: Image(image: AssetImage('assets/logo.png')),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30.0),
            child: TextInput(inputController: nameInput, label: 'আপনার নাম'),
          ),
          ElevatedButton(
            onPressed: () {
              if (_formKey.currentState!.validate()) {
                _register(context);
              }
            },
            child: Text('Next'),
          )
        ]),
      ),
    );
  }

  void _register(BuildContext context) {}
}

class TextInput extends StatelessWidget {
  const TextInput({
    Key? key,
    required this.inputController,
    required this.label,
  }) : super(key: key);

  final TextEditingController inputController;
  final String label;

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      controller: inputController,
      keyboardType: TextInputType.text,
      decoration: const InputDecoration(
        border: UnderlineInputBorder(),
        prefixIcon: Icon(Icons.phone),
        labelText: label,
      ),
      validator: (value) {
        return Validation.required(value);
      },
    );
  }
}

But I got this error:

enter image description here

What is wrong in my code? Is there any problem in my approach or should I stop thinking to refactor my code as I do? Please also suggest if there is any smarter way to make code more clean and manageable.

Upvotes: 0

Views: 1566

Answers (3)

Pradeep Tharu
Pradeep Tharu

Reputation: 11

import 'package:flutter/material.dart';
import 'package:qa_lint/core/utils/constants/constants.dart';

Widget customTextFormField(
    {IconData? prefixIcon,
    required context,
    required String? labelText,
    TextStyle? labelStyle,
    EdgeInsetsGeometry? contentPadding,
    required String? Function(String?)? validator,
    TextInputType? keyboardType,
    required TextInputAction? textInputAction,
    ValueChanged<String>? onFieldSubmitted,
    bool obscureText = false,
    required TextEditingController? controller,
    bool isTablet = false,
    Widget? suffixIcon}) {
  final orientations = MediaQuery.of(context).orientation;

  return Container(
    margin: const EdgeInsets.symmetric(horizontal: 12),
    padding: const EdgeInsets.all(10),
    child: OrientationBuilder(
      builder: (context, orientation) {
        final deviceOrantation = MediaQuery.of(context).orientation;
        bool isLandscape = deviceOrantation == Orientation.landscape;
        return TextFormField(
          style: TextStyle(
              fontSize: tabletIs(context)
                  ? isLandscape
                      ? mediaQueryHeight(context) * 0.028
                      : mediaQueryHeight(context) * 0.022
                  : mediaQueryHeight(context) * 0.022),
          controller: controller,
          obscureText: obscureText,
          validator: validator,
          keyboardType: keyboardType,
          textInputAction: textInputAction,
          onFieldSubmitted: onFieldSubmitted,
          decoration: InputDecoration(
            errorStyle: TextStyle(
              fontSize: defultFontSize(context),
            ),
            suffixIcon: suffixIcon,
            prefixIcon: prefixIcon != null
                ? Icon(
                    prefixIcon,
                    color: Colors.black54,
                    size: tabletIs(context)
                        ? isLandscape
                            ? mediaQueryHeight(context) * 0.04
                            : iconSizeTablet(context)
                        : iconSizeMobile(context),
                  )
                : null,
            labelText: labelText,
            labelStyle: labelStyles(
              fontSize: tabletIs(context)
                  ? (orientations == Orientation.landscape
                      ? tabletLandscapeFontSize(context)
                      : tabletFontSize(context))
                  : defultFontSize(context),
            ),
            contentPadding: tabletIs(context)
                ? isLandscape
                    ? EdgeInsets.all(
                        paddingTablet30(context),
                      )
                    : EdgeInsets.all(paddingTablet30(context))
                : EdgeInsets.all(paddingMobile10(context)),
            border: OutlineInputBorder(
              borderSide: const BorderSide(
                style: BorderStyle.solid,
              ),
              borderRadius: BorderRadius.circular(24),
            ),
            hoverColor: customPrimarySwatch,
            focusedBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(24),
              borderSide: const BorderSide(
                color: customPrimarySwatch,
              ),
            ),
          ),
        );
      },
    ),
  );
}

Custom TextFormField is the best way to use in your project with responsive

Upvotes: -1

Willie Nandi
Willie Nandi

Reputation: 637

Since you are using a variable in InputDecoration, you should not declare InputDecoration with const keyword.

Upvotes: 1

Denzel
Denzel

Reputation: 1109

Oh I see so you have this

            class TextInput extends StatelessWidget {
          const TextInput({
            Key? key,
            required this.inputController,
            required this.label,
          }) : super(key: key);
        
          final TextEditingController inputController;
          final String label;
        
          @override
          Widget build(BuildContext context) {
    
            return TextFormField(
              controller: inputController,
              keyboardType: TextInputType.text,
    // Notice the const here right?
    // So the idea is that decoration objects could rebuild to either change one thing or the other, so 'label' here cannot be a constant
//So to solve this InputDecoration should not have const.
              decoration: const InputDecoration(
                border: UnderlineInputBorder(),
                prefixIcon: Icon(Icons.phone),
                labelText: label,
              ),
              validator: (value) {
                return Validation.required(value);
              },
            );
          }
        }

Upvotes: 2

Related Questions