Flutter Declutter : Using inputDecorationTheme

Flutter Declutter : Using inputDecorationTheme

Episode 1 of an ongoing series to make flutter code more beautiful !

This article is adopted from the following video

So, if you believe that watching/listening is more beneficial than reading ! that video should cover all the same content .

Also , you can find the full code for this tutorial on Github here

The Clutter

Building a form is a common task in almost any mobile application, those forms often contain TextFields or TextFormFields that most of the times will have the same decoration and style. Hence, you'll need to define decoration property for each field and that will result in lots of redundant code and lots of copy/paste operations, and as y'all know as long as you're copying and pasting .. you're doing something wrong.

Here's an example form

Screenshot_20220708_085910.png

And below is the code for a form with just 2 TextFormFields

import 'package:flutter/material.dart';

class ClutteredForm extends StatelessWidget {
  const ClutteredForm({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        leading: BackButton(
          color: Colors.black,
        ),
        actions: [
          IconButton(
              onPressed: () {}, icon: Icon(Icons.check, color: Colors.black))
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          children: [
            SizedBox(
              height: 20,
            ),
            TextFormField(
              decoration: InputDecoration(
                label: Text("First Name"),
                // by making this property tru , the label of the TextFormField will
                //ALWAYS float to the top of the field with a nice animation
                floatingLabelBehavior: FloatingLabelBehavior.always,
                // specify the shape and color of the border of the TextFormField
                disabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.blueGrey,
                    width: 1,
                  ),
                ),
                enabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.grey,
                    width: 1,
                  ),
                ),
                focusedBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.blue,
                    width: 1,
                  ),
                ),
                errorBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.redAccent,
                    width: 1,
                  ),
                ),
                focusedErrorBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.orangeAccent,
                    width: 1,
                  ),
                ),
                errorStyle: TextStyle(
                  color: Colors.red,
                  fontSize: 16,
                ),
              ),
            ),
            SizedBox(
              height: 20,
            ),
            TextFormField(
              decoration: InputDecoration(
                label: Text("Last Name"),
                // by making this property tru , the label of the TextFormField will
                //ALWAYS float to the top of the field with a nice animation
                floatingLabelBehavior: FloatingLabelBehavior.always,
                // specify the shape and color of the border of the TextFormField
                disabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.blueGrey,
                    width: 1,
                  ),
                ),
                enabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.grey,
                    width: 1,
                  ),
                ),
                focusedBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.blue,
                    width: 1,
                  ),
                ),
                errorBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.redAccent,
                    width: 1,
                  ),
                ),
                focusedErrorBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(
                    color: Colors.orangeAccent,
                    width: 1,
                  ),
                ),
                errorStyle: TextStyle(
                  color: Colors.redAccent,
                  fontSize: 16,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

it's a mess, right?

suppose you have more than 2 fields, say 5, or maybe more! it'll be a nightmare!

so, how do you declutter this mess? Enter inputDecorationTheme

InputDecorationTheme class

Defines the default appearance of InputDecorators.

This class is used to define the value of ThemeData.inputDecorationTheme. The InputDecorator, TextField, and TextFormField widgets use the current input decoration theme to initialize null InputDecoration properties.

if you don't know about theming in flutter , checkout this amazing talk from Flutter Interact '19 by Yasmine Evjen

Decluttering Steps

Step #1

Create a file theme.dart with the following code in it

import 'package:flutter/material.dart';

class MyInputDecorationTheme {
  // A method that returns InputDecorationTheme to pass it to the inputDecorationTheme
  // property of the ThemeData Class  in main.dart
  InputDecorationTheme myInputDecorationTheme() => InputDecorationTheme(
        // by making this property tru , the label of the TextFormField will
        //ALWAYS float to the top of the field with a nice animation
        floatingLabelBehavior: FloatingLabelBehavior.always,
        // specify the shape and color of the border of the TextFormField
        disabledBorder: _buildOutlineInputBorder(Colors.blueGrey),
        enabledBorder: _buildOutlineInputBorder(Colors.grey),
        focusedBorder: _buildOutlineInputBorder(Colors.blue),
        errorBorder: _buildOutlineInputBorder(Colors.red),
        focusedErrorBorder: _buildOutlineInputBorder(Colors.orangeAccent),
        errorStyle: _buildTextStyle(Colors.red),
      );

  OutlineInputBorder _buildOutlineInputBorder(Color color) =>
      OutlineInputBorder(
        borderRadius: BorderRadius.circular(20),
        borderSide: BorderSide(
          color: color,
          width: 1,
        ),
      );

  TextStyle _buildTextStyle(Color color, {double fontSize = 16}) => TextStyle(
        color: color,
        fontSize: fontSize,
      );
}

inputDecorationTheme has a plethora of properties, consult the documentation to learn more about the purpose of each property.

Step #2

Define the theme property in MaterialApp widget :

    return MaterialApp(
      title: 'Declutter Flutter : Episode 1 ',
      theme: ThemeData(
        inputDecorationTheme: MyInputDecorationTheme().myInputDecorationTheme(),
      ),
      home: DeclutteredForm(),
    );

Now, all of your TextFields or TextFormFields will look exactly the same.

That's it for today's episode, I hope you found it helpful.

You can find the full code for this tutorial on Github here