IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> flutter学习日记 -- bilibili app -- day01 书写登录界面 -> 正文阅读

[移动开发]flutter学习日记 -- bilibili app -- day01 书写登录界面

Flutter 学习日记 – 制作bilibili app day01

首先要做的是登录界面如下,在这里插入图片描述

如果后期有空我再进行后台开发,并逐步完善app功能,现在首先先做前台样式。

打开Android Studio 新建flutter 工程 bilibili, 代码结构如下:在这里插入图片描述,core 负责核心的一些功能,后期的http请求都放在core里,这里我在core目录目前新建了两个文件夹,一个是extension 给我们的屏幕做适配,也就是说类似于微信小程序rpx功能,如果你对这个工具感兴趣,请阅读codedr why
Pages 文件夹里存放着我们所有的页面,目前我们首先要完成login 页面。在完成页面之前,我们首先在main.dart 文件创立我们的基本骨架

import 'package:bilibili/pages/theme_style/app_theme.dart';
import 'package:bilibili/core/extension/share.dart';
import 'package:bilibili/pages/login/login.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

main() {
  runApp(MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    SizeFit.initSize();  ## 这里调用的我们extension 里面的初始化屏幕方法,以便后期我们使用里面的方法
    return MaterialApp(
      home: LoginScreen(),  ## 这里暂且这样书写,后期用到路由再将此替换即可
      theme: AppThemeStyle.normalTheme ## 我们创建的 theme 主题 存放在一个单独文件夹里,在pages/theme_style 存放我们的主题
    );
  }

}

打开 pages/theme/ 创建一个新文件 app_theme.dart 在里面我们创立 一个类 AppThemeStyle 里面友静态方法 normalTheme 用来返回我们的主题
这里注意导入

import 'package:bilibili/core/extension/int_extension.dart';
import 'package:flutter/material.dart';
class AppThemeStyle {
  static Color normalColor = Colors.white;
  static double headline1FontSize = 25.px;
  static double headline2FontSize = 30.px;
  static double headline3FontSize = 45.px;
  static double headline4FontSize = 15.px;

  static final normalTheme = ThemeData(
      primaryColor: normalColor,
      accentColor: normalColor,
      canvasColor: Color.fromARGB(255, 230, 230, 230),
      textTheme: TextTheme(
          headline1: TextStyle(fontSize: headline1FontSize, color: Colors.black87),
          headline2: TextStyle(fontSize: headline2FontSize, color: Colors.black),
          headline3: TextStyle(fontSize: headline3FontSize, color: Colors.black),
          headline4: TextStyle(fontSize: headline4FontSize, color: Colors.black)
      )
  );

}

在这里我们的主题就创建完毕了,将主题单独抽放到文件,我们后期维护代码也十分方便。
打开pages/login 创立 login.dart

 import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'login_content.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Center(child: Text("密码登录"))),
      body: LoginContent(),
    );
  }
}

这里注意,我们页面的内容,在新创立的文件 login_content 进行书写,这样代码看起来比较简洁,在pages/login 创立login_content 页面 新建一个StatefulWidget,取名为LoginContent()
这里 我将登录页面划分为两个模块,这是上半模块:在这里插入图片描述

观察页面结构,上半模块和下半模块就是column 两个子组件,也就是说,我们只需要一个column 将两个包裹即可,中间那部分我们用sizebox 设置一个height 即可 在_LoginContentState build 方法里面 return

Column(
  children: [
    LoginHeader.buildLoginHeader(context, imageNum, dividerColor, focusNode),
    SizedBox(height: 50.px,),
    LoginBottom.buildLoginBottom(),
  ],
);

分别创立两个文件用来 管理两个组件, login_header 和 login_bottom

Pages 结构如下 :在这里插入图片描述打开login_header 创立类LoginHeader, 里面存放静态方法buildLoginHeader , 观察 页面,我们发现 这里面也是由一个 column 构成, 最上面图片我们只需要用Image.asset,将图片从本地加载
Image.asset("images/login/login_image0$imageNum.jpg", fit: BoxFit.fitWidth,)
其中这里用了字符串拼接,这是由于页面你输入密码时登录图片会自动切换。这种效果在这里插入图片描述

所以我将imageNum 作为参数进行传递

输入框我们则单独封装一个widget
输入框是明显的Row 左边是 container 包裹一个 Text 右边则是一个textfields, 实现代码如下

Container(
  padding: EdgeInsets.zero,
  color: Colors.white,
  child: Row(
    children: [
      Container(
          width: 150.px,
          // color: Colors.red,
          padding: EdgeInsets.only(left: 30.px),
          child: Text(title, style: Theme.of(context).textTheme.headline2)
      ),
      Expanded(
        child: TextField(
          focusNode: focusNode, ## focusNode 做为可选参数传递,判断是否点击密码输入框
          cursorColor: Colors.pinkAccent, ## 光标颜色
          obscureText: obscureText, ## 参数传递,若为true 则隐藏输入内容
          cursorHeight: 40.px, ## 光标高度
          decoration: InputDecoration(
            hintText: hintText, ## 作为参数传递, 帮助提示文字
            hintStyle: Theme.of(context).textTheme.headline1!.copyWith(color: dividerColor), ## 这里使用了主题里面的headline 的字体样式
            border: InputBorder.none, ),
        ),
      ),
    ],
  ),

整体代码

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:bilibili/core/extension/int_extension.dart';

class LoginHeader {

  static Widget buildLoginHeader(BuildContext context, imageNum, Color dividerColor, FocusNode focusNode) {
    return Column(
      children: [
        Image.asset("images/login/login_image0$imageNum.jpg", fit: BoxFit.fitWidth,),
        buildInputFields(context, "账号", "请输入手机号或邮箱", false, dividerColor),
        buildDivider(dividerColor),
        buildInputFields(context, "密码", "请输入密码", true, dividerColor, focusNode: focusNode),
        buildDivider(dividerColor)
      ],
    );
}

  static Divider buildDivider(Color dividerColor) => Divider(indent: 30.px, height: 1.px, color: dividerColor,);

  static Widget buildInputFields(BuildContext context, String title, String hintText, bool obscureText, Color dividerColor, {FocusNode? focusNode}) {
    return Container(
      padding: EdgeInsets.zero,
      color: Colors.white,
      child: Row(
        children: [
          Container(
              width: 150.px,
              // color: Colors.red,
              padding: EdgeInsets.only(left: 30.px),
              child: Text(title, style: Theme.of(context).textTheme.headline2)
          ),
          Expanded(
            child: TextField(
              focusNode: focusNode,
              cursorColor: Colors.pinkAccent,
              obscureText: obscureText,
              cursorHeight: 40.px,
              decoration: InputDecoration(
                hintText: hintText,
                hintStyle: Theme.of(context).textTheme.headline1!.copyWith(color: dividerColor),
                border: InputBorder.none, ),
            ),
          ),
        ],
      ),
    );
  }

}

封装完 header 后 我们继续封装 bottom bottom 就是两个按钮 十分简单 这里就放代码了

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:bilibili/core/extension/int_extension.dart';

class LoginBottom {
  static final mainColor = Colors.pinkAccent;
  static final fixedSize = Size(325.px, 70.px);

  static Widget buildLoginBottom() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        OutlinedButton(
          onPressed: () {},
          child: Text("注册", style: TextStyle(color: mainColor),),
          style: OutlinedButton.styleFrom(side: BorderSide(color: mainColor), fixedSize: fixedSize),
        ),
        TextButton(
            onPressed: () {},
            child: Text("登录", style: TextStyle(color: Colors.white),),
            style: TextButton.styleFrom(backgroundColor: mainColor, fixedSize: fixedSize)
        )
      ],
    );
  }

}

最后我们login_content是这样的

import 'package:bilibili/pages/login/login_bottom.dart';
import 'package:bilibili/pages/login/login_header.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:bilibili/core/extension/int_extension.dart';

class LoginContent extends StatefulWidget {
  const LoginContent({Key? key}) : super(key: key);

  @override
  _LoginContentState createState() => _LoginContentState();
}

class _LoginContentState extends State<LoginContent> {
  FocusNode focusNode = FocusNode();
  final dividerColor = Color.fromARGB(255, 190, 190, 190);
  int imageNum = 1;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    focusNode.addListener(() { 
      if(focusNode.hasFocus) setState(() {imageNum = 2;});
      else setState(() {imageNum = 1;});
    }); ## 这里对光标焦点进行监听, 之前我们将focusNode 传递到密码框中,所以这里就可以判断focusNode.hasFocus 属性作为点击密码切换的依据
  }
  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    focusNode.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        LoginHeader.buildLoginHeader(context, imageNum, dividerColor, focusNode),
        SizedBox(height: 50.px,),
        LoginBottom.buildLoginBottom(),
      ],
    );
  }


}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-25 12:19:37  更:2021-08-25 12:19:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:36:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码