This is the first day of my participation in Gwen Challenge

Login page is very common in App development. This paper introduces the use of TextField component of text box through the development of login page, and realizes the personalized text box setting by using the decorative properties of text box.

Today is “61” children’s day, the size of the circle of friends brush a noisy circle of friends, see a very touched words: “when I was a child, happiness always come so simple; As I grow older, simplicity leads to more happiness. For simple programmers, I believe in the code of the world can find a lot of happiness, I wish you all a happy holiday (specially changed the navigation bar)!

The business logic

In order to demonstrate the login jump, a simple button was made in the classification browse first, click to jump to the login page. In actual apps, some operations that require login are usually triggered and then the login page is redirected.

Layout analysis

The interface is shown in the image above. From the point of view of the interface, the overall content area is centered. The layout of the content is a simple column layout, including a Logo (usually an App icon) at the top, two text input fields below, and finally a login button. The overall layout is relatively simple. Use a Column embedded under Center for Column layout.

Picture circle cropping

There are two ways to circle images in Flutter. One is to use the outer container by clipping squares around the circle. The second is to use the built-in CircleAvatar. However, CircleAvatar is used for avatars, so the container is used for circular clipping. Wrap a method to get a round image _getRoundImage, passing in the name of the image resource and the length of the square side as follows:

Widget _getRoundImage(String imageName, double size) {
    return Container(
      width: size,
      height: size,
      clipBehavior: Clip.antiAlias,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(size / 2)),
      ),
      child: Image.asset(
        imageName,
        fit: BoxFit.fitWidth,
      ),
    );
  }
Copy the code

The BoxDecoration is used to set the border to a circular border with a radius half the length of the side. However, one additional property that needs to be set is clipBehavior, which is an edge clipping type and is unclipped by default. Clip.antiAlias is used for clipping. This method provides the best clipping effect but consumes more resources. Other clipping methods are as follows:

  • Clip. HardEdge: As the name suggests, this method is rough, but the fastest to cut.
  • Clip. AntiAliasSaveLayer: The finest cutting, but very slow and not recommended;
  • Clip.none: Default, no clipping is done if the content area does not exceed the container boundary. Other clipping methods are needed to prevent content overflow if the content is out of bounds.

Round flat button

It should be mentioned here that the FlatButton used before Flutter 2.0 was the FlatButton. This button was simple to use but unsatisfactory in many situations. Therefore, TextButton was introduced after Flutter 2.0. TextButton has an extra style to decorate the button style. Specific can see official document. Here, we need to set the background color of the button to the theme color, and the text color of the button to white. At the same time, we need to cut it into rounded corners, so we still use the boundary arc of the Container to achieve this. Note that the default button width is based on the content, so in order to fill the screen, we set the Container width to double. Infinity. The code looks like this:

Widget _getLoginButton() {
    return Container(
      height: 50,
      width: double.infinity,
      margin: EdgeInsets.all(10),
      decoration: BoxDecoration(
        color: Theme.of(context).primaryColor,
        borderRadius: BorderRadius.circular(4.0),
      ),
      child: TextButton(
        style: ButtonStyle(
          foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
          backgroundColor:
              MaterialStateProperty.all<Color>(Theme.of(context).primaryColor),
        ),
        child: Text(
          'login',
        ),
        onPressed: () {
          print(
              'Login: username=${_username.trim()}, password=${_password.trim()}'); },),); }Copy the code

The button click callback event is onPressed and simply prints the contents of the form.

TextField text box

TextField is a text input box provided by Flutter. There are many properties of TextField, including the following:

  • KeyboardType: specifies the keyboardType, such as numbers, letters, phone number, email address, and date. The keyboardType that matches the form content provides input efficiency and improves user experience.
  • Controller: TextEditingController object. The TextEditingController is used to control the initial value of the text box and clear the contents.
  • ObscureText: Whether input needs to be hidden. If true, input is displayed as dots, usually with a password.
  • You can specify the front icon, border type, rear component, etc., so you can use decoration to get the desired text box style.
  • FocusNode: focus. You can use this to control whether the text box gets focus, which is similar to previous and next input control.
  • OnChanged: The input value changes the event callback, which is usually used to implement bidirectional binding.

In this case, we use a front icon to indicate the type of input, such as the phone icon for entering the phone number, and the lock for password. It also uses an Offstage as a post-placed component that can be clicked to clear content after input. The Offstage component controls whether the component is displayed with a property Offstage, so we can hide it when there is no content and display it when there is input.

To improve code reuse, we use a method to get a generic text box. Here we use the Container wrapper to control the margins and the delimiters under the text box:

Widget _getInputTextField(
    TextInputType keyboardType, {
    FocusNode focusNode,
    controller: TextEditingController,
    onChanged: Function,
    InputDecoration decoration,
    bool obscureText = false,
    height = 50.0, {})return Container(
      height: height,
      margin: EdgeInsets.all(10.0),
      child: Column(
        children: [
          TextField(
            keyboardType: keyboardType,
            focusNode: focusNode,
            obscureText: obscureText,
            controller: controller,
            decoration: decoration,
            onChanged: onChanged,
          ),
          Divider(
            height: 1.0,
            color: Colors.grey[400[, [, [, [, [, [ }Copy the code

The complete code

class _LoginPageState extends State<LoginPage> {
  //TextEditingController can specify an initial value using the text property
  TextEditingController _usernameController = TextEditingController();
  TextEditingController _passwordController = TextEditingController();
  String _username = ' ', _password = ' ';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('login'),
        brightness: Brightness.dark,
      ),
      body: Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _getRoundImage('images/logo.png'.100.0),
            SizedBox(
              height: 60,
            ),
            _getUsernameInput(),
            _getPasswordInput(),
            SizedBox(
              height: 10,
            ),
            _getLoginButton(),
          ],
        ),
      ),
    );
  }

  Widget _getUsernameInput() {
    return _getInputTextField(
      TextInputType.number,
      controller: _usernameController,
      decoration: InputDecoration(
        hintText: "Enter your mobile phone number",
        icon: Icon(
          Icons.mobile_friendly_rounded,
          size: 20.0,
        ),
        border: InputBorder.none,
        // Use GestureDetector to implement gesture recognition
        suffixIcon: GestureDetector(
          child: Offstage(
            child: Icon(Icons.clear),
            offstage: _username == ' ',),// Click to clear the text box
          onTap: () {
            this.setState(() {
              _username = ' '; _usernameController.clear(); }); },),),// Use onChanged to complete bidirectional binding
      onChanged: (value) {
        this.setState(() { _username = value; }); }); } Widget _getPasswordInput() {return _getInputTextField(
      TextInputType.text,
      obscureText: true,
      controller: _passwordController,
      decoration: InputDecoration(
        hintText: "Enter your password",
        icon: Icon(
          Icons.lock_open,
          size: 20.0,
        ),
        suffixIcon: GestureDetector(
          child: Offstage(
            child: Icon(Icons.clear),
            offstage: _password == ' ',
          ),
          onTap: () {
            this.setState(() {
              _password = ' ';
              _passwordController.clear();
            });
          },
        ),
        border: InputBorder.none,
      ),
      onChanged: (value) {
        this.setState(() { _password = value; }); }); }// The code listed above is omitted

}
Copy the code

Page jump

On the top login button, we added a click event, click and then jump to the login page, the response code of the button is shown below. This is the easiest way to jump to a page, using the Push method of the Navigator Navigator. We’ll see how to jump to a page via routing, which is more elegant.

/ /...
onPressed: () {
  Navigator.of(context).push(
    MaterialPageRoute(builder: (context) => LoginPage()),
  );
},
/ /...
Copy the code

conclusion

From the point of view of the code, although the function is implemented, the code for building the username and password is very similar. Is there any way to further improve the code reuse rate and build a more generic form component? In the next chapter we will show how to encapsulate.