Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Foreword: Five days ago, I posted a lot of UI for the login interface and launched a poll:Juejin. Cn/post / 701092…

From that day on, I found UI little sister, using a cup of milk tea sent to the hand, in exchange for the little sister’s cut. Then I started to use the Flutter implementation after two days of work. However, this design does not perform well on many Android phones. As a conscience up😭, I must not give it to you

Half done, found that the effect is not good, I have been packaged, I think we can download a change can be commercial

Then I chose another one, this time adhering to the principle of men wear a little green and I achieved it!!

Effect:

A little tired, but still encapsulate the data, so give me a thumbs up 😘 code data basic encapsulation is completed, interface adaptation is also done, need code in the end of the article, you can change yourself into the project, please carefully read the article, otherwise it may not run up 😜

Analysis:

1. Data encapsulation

2. Guide page · Swipe left and right

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

4. Boot page · Judgment slide to the last jump to the login interface, and remove from the memory

5. Home page · Input box processing

6. Homepage · Forgotten password, registration button, login button processing

Note: Screen adaptation uses flutter_screenutil: ^5.0.0.

1. Data encapsulation

For better maintenance, we need to encapsulate data, and if there is an interface, we also need to encapsulate the interface table:

class TextData { static String welcome = "Holding spave\nfor collaborative\nconversation"; Static String login = "login "; Static String name = "username"; Static String password = "password"; static String register = "Not a member yet? Sing up! \n Forgot password"; // Login button static String welcomeImage = "images/welcome.png"; Static String loginBackImage = "images/ loginback.png "; // Login background}Copy the code

2. Swipe left and right

In this scenario, PageView is very suitable. In order to fit, we need to use Stack to wrap PageView:

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

In this case, I selected PageView.Builder () for better modification. You only need to edit the bootwidget you need.

Look at the code:

1. We need to use a PageController to control PageView

PageController _pageController;
Copy the code

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

int _currentPage = 0;
Copy the code

3. For your own use, you can use an array to store the Widget

4. Process data as the page slides 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: color.white)),],); }, onPageChanged: (int index) { setState(() { _currentPage = index; // save the current page subscript}); }, itemCount: 5, // change your Widget scrollDirection: Axis. Horizontal, Reverse: false, Controller: _pageController,),Copy the code

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

I suggest you can add the current interface subscript in the diamond, but UI sister did not help me draw 😭

This is how to draw a diamond with an arc:

We define a double to hold the M coefficient:

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

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

Four points 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. It usually doesn’t go through P1 or P2. These two points just provide directional information there. The distance between P0 and P1 determines “how long” the curve moves toward P2 before turning toward P3.

Based on this formula, I calculated 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*(percent0.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*(percent0.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
  );
}
Copy the code

We also need to calculate the drop point each time :(adaptive, XDM is safe to eat)

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

int preInteger = 0;
Copy the code

Then listen on the Controller of PageView:

@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(() {});
  });
}
Copy the code

Use transform.translate to locate a path:

Calculate the value 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;
Copy the code

And most importantly:

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

That’s the key to finding the x-coordinate!

Here is the code to use:

Transform.translate(
  offset: Offset(offSetX, 0), ///OffSetx is used for positioning
  child: Stack(
    children: [
      CustomPaint(
        painter: BesselView( ///This is the animation path calculated above
            radius: radius,
            percent: percent,
            isToRight: isToRight,
            color: Colors.white),
      ),
      // currentPage.toString(),style: TextStyle(fontSize: 50.sp),],),)Copy the code

Complete implementation, we can look at the source code

4. Boot page · Judgment slide to the last jump to the login interface, and remove from the memory

In PageView onPageChanged, if the index on the sliding is outside the defined Widget array, we jump:

PushReplacement: The current page is the target page (that is, there are only two pages in the stack, and the current page is returned as the home page). 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 the boot page
    Navigator.pushReplacement(context,
        MaterialPageRoute(builder: (context) => LoginPage()));
  }
  setState(() {
    _currentPage = index;
  });
},
Copy the code

5. Home page · Input box processing

This is pretty routine, but I’ve encapsulated it 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),
      // The inner margin of the content affects the 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,
  );
}
Copy the code

6. Homepage · Forgotten password, registration button, login button processing

Here is mainly to tell you some not commonly used properties of Text, as well as a simple processing of the string, tell the small white:

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

Decoration: TextDecoration. Underline, underline text

Decoration: TextDecoration lineThrough, delete the line

Dashed and overlaid lines:

decoration: TextDecoration.overline,

decorationStyle: TextDecorationStyle.dashed,

Well, it took 5 days to fix it and finally it’s done! Give a praise elder brother sauce 😜

Need source code to leave a message in the comment area, will soon reply (do not want to build a warehouse, next time a comprehensive put out)🐮🐴