After 5 days, I finally made it!!! (cool guide page, login interface)

Keywords: Flutter

Small knowledge, big challenge! This article is participating“ Essential knowledge for programmers ”Creative activities.

Preface: five days ago, I released many UI of login interface and initiated voting: https://juejin.cn/post/7010922696988966919

From that day on, I found my ui little sister and used a c up of milk tea in exchange for her cut. Then, after two days of work, I started to use fluent to implement it. However, this design drawing does not perform well on many android phones. As a result, it is not good 😭, Then I must not give it to everyone

Half done, found that the effect is not good, I have encapsulated it, so I think we can download it and change it

Then I chose another one. This time, adhering to the principle that men bring a little green, I realized it!!!

design sketch:


I'm a little tired, but I still encapsulate the data, so give me a compliment 😘 The code data is basically encapsulated, and the interface adaptation is also done. The code that needs to be modified can be put into the project at the end of the article. Please read the article carefully, otherwise it may not run 😜

analysis:

1. Data encapsulation

2. Guide page · slide left and right

3. Guide page · bottom animation processing (follow animation)

4. The guide page · judgment slides to the last, jumps to the login interface, and removes it from memory

5. Home page · input box processing

6. Home page · forget password, register button and login button

Note: for screen adaptation, use the shutter_ Screenutil: ^ 5.0.0 (you can write it yourself, so you only need to write what you need, and the code will be clearer)

1. Data encapsulation

For better subsequent maintenance, we need to encapsulate data. If there is an interface, we also need to encapsulate the interface table:

class TextData {
  static String welcome = "Holding spave\nfor collaborative\nconversation"; //Welcome speech
  static String login = "Login"; // Sign in
  static String name = "username"; //Name box prompt
  static String password = "password";
  static String register = "Not a member yet? Sing up!\n Forgot password"; //Login button
  static String welcomeImage = "images/welcome.png"; //Welcome interface background
  static String loginBackImage = "images/loginBack.png"; //Login interface background
}

2. Slide left and right

It can be seen from the rendering that it is a Widget that can move left and right. In this scenario, PageView is very suitable. In order to adapt, we need to use Stack to wrap PageView:

return Scaffold(
    body: Stack(
    childern:[
    ....
    ]
  ));

Here, for better modification, I choose PageView.builder(). You only need to edit the required boot Widget.

Look at the code:

1. We need to use a PageController to control PageView

PageController _pageController;

2. Define an int variable to record the current boot page

int _currentPage = 0;

3. When you use it yourself, you can use the array to store the Widget, and the replacement comments are given in the code 😉

4. Process the data when the page slide changes in onPageChanged

PageView.builder(
  itemBuilder: (context, index) {
    return Stack(
      children: [
        Container(
          width: 1.sw,
          height: 1.sh,
          child: Image.asset(
            TextData.welcomeImage,
            fit: BoxFit.fill,
          ),
        ),
        Positioned(
          left: 60.w,
          bottom: 200,
          child: Text(TextData.welcome,
              style: TextStyle(fontSize: 50.sp, color: Colors.white)), ///The blogger writes freely and everyone modifies it by themselves
        ),
      ],
    );
  },
  onPageChanged: (int index) {
    setState(() {
      _currentPage = index; ///Saves the subscript of the current page
    });
  },
  itemCount: 5, //Change to your own Widget
  scrollDirection: Axis.horizontal,
  reverse: false,
  controller: _pageController,
),

3. Guide page · bottom animation processing (follow animation)

I suggest adding the subscript of the current interface to the diamond. Unfortunately, my little sister ui didn't draw it for me 😭

This is how to draw a diamond with an arc:

We define a double to store the M coefficient:

double radius = 1.sw / 20; //This value is for adaptation

We also need to analyze and process the animation during motion, which is very similar to the third-order Bezier curve:

p0, p1, p2 and p3 define cubic Bezier curves in plane or three-dimensional space. The curve starts from p0 to p1 and goes from p2 to p3. Generally, it does not pass through p1 or p2; These two points only provide direction information there. The distance between p0 and p1 determines the "length" of the curve towards p2 before it turns to p3.

According to this formula, I calculate the animation path:

void _canvasBesselPath(Path path) {

  Point p1 = Point(x: radius*2,y: radius);
  Point p2 = Point(x: radius,y: radius*2);
  Point p3 = Point(x: 0,y: radius);
  Point p4 = Point(x: radius,y: 0);

  if (isToRight) {
    if (percent <= 0.2) {
      p1.x = radius*2 + radius*percent/0.2;
    } else if (percent <= 0.4) {
      p4.x = p2.x = radius + radius*(percent-0.2)/0.2;
      p1.x = p2.x + radius*2;
    } else if (percent <= 0.6) {
      p3.x = radius*(percent - 0.4)/0.2;
      p4.x = p2.x = p3.x + radius*2;
      p1.x = radius*4;
    } else if (percent <= 0.8) {
      p3.x = radius + radius*(percent - 0.6)/0.2;
      p4.x = p2.x = radius*3;
      p1.x = radius*4;
    } else if (percent <= 0.9) {
      p3.x = 2*radius+radius*(percent - 0.8)/0.3;
      p4.x = p2.x = radius*3;
      p1.x = radius*4;
    } else if (percent <= 1.0) {
      p3.x = 2*radius+radius*(1 - percent)/0.3;
      p4.x = p2.x = radius*3;
      p1.x = radius*4;
    }
  } else {
    if (percent <= 0.2) {
      p3.x = - radius*percent/0.2;
    } else if (percent <= 0.4) {
      p3.x = -radius - radius*(percent-0.2)/0.2;
      p4.x = p2.x = p3.x + 2*radius;
    } else if (percent <= 0.6) {
      p3.x = - 2*radius;
      p4.x = p2.x = - radius*(percent - 0.4)/0.2;
      p1.x = p2.x + radius*2;
    } else if (percent <= 0.8) {
      p3.x = -2*radius;
      p4.x = p2.x = -radius;
      p1.x = p2.x + radius*2 - radius*(percent - 0.6)/0.2;
    } else if (percent <= 0.9) {
      p3.x = -2*radius;
      p4.x = p2.x = -radius;
      p1.x = p2.x + radius - radius*(percent - 0.8)/0.4;
    } else if (percent <= 1.0) {
      p3.x = -2*radius;
      p4.x = p2.x = -radius;
      p1.x = p2.x + radius - radius*(1 - percent)/0.4;
    }
  }

  final p1Radius = p2.y - p1.y;
  final p24LeftRadius = p2.x - p3.x;
  final p24RightRadius = p1.x - p2.x;
  final p3Radius = p2.y - p3.y;
  path.moveTo(p1.x, p1.y);
  path.cubicTo(
      p1.x, p1.y + p1Radius*M,
      p2.x + p24RightRadius*M, p2.y,
      p2.x, p2.y
  );
  path.cubicTo(
      p2.x - p24LeftRadius*M, p2.y,
      p3.x, p3.y + p3Radius*M,
      p3.x, p3.y
  );
  path.cubicTo(
      p3.x, p3.y - p3Radius*M,
      p4.x - p24LeftRadius*M, p4.y,
      p4.x, p4.y
  );
  path.cubicTo(
      p4.x + p24RightRadius*M, p4.y,
      p1.x , p1.y - p1Radius*M,
      p1.x, p1.y
  );
}

We also need to calculate the landing point of each time: (it has been adaptive, XDM can rest assured)

Define an int variable to compare with the current page subscript:

int preInteger = 0;

Then listen to the PageView controller:

@override
void initState() {
  super.initState();
  _pageController = PageController(viewportFraction: 1);
  _pageController.addListener(() {
    curPosition = _pageController.page;
    if (curPosition.toInt() == curPosition) {
      preInteger = curPosition.toInt();
    } else if (curPosition > preInteger) {
      isToRight = true;
    } else {
      isToRight = false;
    }
    setState(() {});
  });
}

Use Transform.translate to locate the path (using):

Calculate the value of offSetX for positioning, abscissa:

double percent;
if (isToRight) {
  percent = curPosition - curPosition.toInt();
} else {
  percent = 1 - curPosition + curPosition.toInt();
}

double offsetPercent;
if (isToRight) {
  if (percent <= 0.8) {
    offsetPercent = curPosition.toInt() + percent / 0.8;
  } else {
    offsetPercent = curPosition.ceil().toDouble();
  }
} else {
  if (percent <= 0.8) {
    offsetPercent = curPosition.ceil() - percent / 0.8;
  } else {
    offsetPercent = curPosition.toInt().toDouble();
  }
}

double deviceWidth = 1.sw;
double offSetX = deviceWidth * 0.2 +
    (deviceWidth - radius * 2 - deviceWidth * 0.2) * offsetPercent / 5 - 20;

most important of all:

double offSetX = deviceWidth * 0.2 +
    (deviceWidth - radius * 2 - deviceWidth * 0.2) * offsetPercent / 5 - 20;

This sentence is the key to locate and calculate the abscissa!

Here is the usage code:

Transform.translate(
  offset: Offset(offSetX, 0), ///offSetx for positioning
  child: Stack(
    children: [
      CustomPaint(
        painter: BesselView( ///This is the animation path calculated above
            radius: radius,
            percent: percent,
            isToRight: isToRight,
            color: Colors.white),
      ),
      // Text(currentPage.toString(),style: TextStyle(fontSize: 50.sp),) originally wanted to write subscripts by yourself, but the style was annotated if it was difficult to see
    ],
  ),
)

Complete implementation, you can look at the source code

4. The guide page · judgment slides to the last, jumps to the login interface, and removes it from memory

Judge onPageChanged in PageView. When the subscript during sliding exceeds the defined Widget array, we jump to:

pushReplacement jump method: change the current page to the target page (that is, there are only two pages in the stack: the first page and the current page, and the current page will return to the first page naturally). Use the following statement to complete the replacement jump.

onPageChanged: (int index) {
  print("The current page is $index");
  if (index + 1 == 5) {
    print("Jump to home page");
    ///Clear boot page
    Navigator.pushReplacement(context,
        MaterialPageRoute(builder: (context) => LoginPage()));
  }
  setState(() {
    _currentPage = index;
  });
},

5. Home page · input box processing

This is very routine, but I have encapsulated this effect for you:

import 'package:flutter/material.dart';

inputTextItem(
    {FocusNode focusNode,
    TextEditingController controller,
    TextInputType textInputType,
    String hintText,
    double hintFontSize,
    double cursorHeight = 2.0,
    ValueChanged onPress,
    bool obscureText = false,
    Key key}) {
  return TextField(
    controller: controller,
    focusNode: focusNode,
    keyboardType: textInputType,
    obscureText: obscureText,
    cursorHeight: cursorHeight,
    decoration: InputDecoration(
      isCollapsed: true,
      contentPadding: EdgeInsets.symmetric(horizontal: 0, vertical: 8),
      //Content inner margin, influence height
      border: InputBorder.none,
      filled: false,
      fillColor: Color.fromARGB(255, 225, 225, 225),
      hintText: hintText,
      hintStyle: TextStyle(fontSize: hintFontSize, color: Colors.grey,
          textBaseline: TextBaseline.alphabetic),
    ),
    onSubmitted: onPress,
  );
}

6. Home page · forget password, register button and login button

Here we mainly want to tell you some properties of Text that are not commonly used, and simply handle the string. Tell Xiaobai:

style: TextStyle(
  color: Colors.white,
  fontSize: 32.sp,
  decoration: TextDecoration.underline,
),

decoration: TextDecoration.underline, text underline

decoration: TextDecoration.lineThrough, delete line

Dashed and dashed lines:

decoration: TextDecoration.overline,

decorationStyle: TextDecorationStyle.dashed,

Well, it took five days to fix it, and it's finally finished! Give me a compliment, brother 😜

If you need the source code, leave a message in the comment area and you will reply soon (you don't want to build a warehouse. Next time, the whole one will be released comprehensively) 🐮🐴

Posted by jarcoal on Wed, 29 Sep 2021 15:34:16 -0700