前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调

【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调

原创
作者头像
卓伊凡
发布2025-01-23 14:24:13
发布2025-01-23 14:24:13
6900
代码可运行
举报
文章被收录于专栏:其他相关技术其他相关技术
运行总次数:0
代码可运行

【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈

章节内容【06】

flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且打包demo做演示

开发背景

上篇我们做了自定义组件,本文继续完善注册相关页面并且实现跳转

闲话不多,开源仓库地址,可以观摩已经写好的代码:

https://gitee.com/youyacao/ff-flutter

demo下载

实战开始

上一篇我们在完成后发现个报错问题,

代码语言:javascript
代码运行次数:0
复制
Target of URI doesn't exist: 'package:ff_flutter/lib/widgets/pinkbutton.dart'.
Try creating the file referenced by the URI, or try using a URI for a file that does exist.

这是提示目录中没有找到pinkbutton.dart文件,相关报错还有很多,但是优雅草卓伊凡的路径是正确的,其实就是package:ff_flutter识别不了了。

这是因为包依赖问题:

如果 ff_flutter 是一个自定义包,确保它已经在 pubspec.yaml 中正确声明。 运行 flutter pub get 更新依赖。

代码语言:javascript
代码运行次数:0
复制
dependencies:
  ff_flutter:
    path: ../ff_flutter  # 根据实际情况调整路径

清理和重建项目:

运行 flutter clean 清理构建缓存。 运行 flutter pub get 获取最新依赖。 重新启动 IDE 或编辑器以确保所有更改生效。

可是当我们运行调试的时候还是报错了,这时候我们再来看

发现问题了,name应该是 name: ff_flutter 才对。

一切就绪后还是报错,依赖也是也正确的,终于我测试,把lib目录去掉,就成功了

想了一下,那么为啥呢,原来我定义目录已经是path: ../ff_flutter 那么package:ff_flutter 已经就到了lib 目录下了,我再去加lib目录那当然要出错啦,问题解决,我们进行下一步

有了注册页面我们做登录页面

新建login.dart

这个应该是登录页面了,但是login画错了,而且下面有切换用户密码登录,那么这里就是短信登录,因此我反馈给ui了 让去整改下,其次login文件名改为smslogin这样会方便知道。

观察了下样式基本一致因此我们复制注册页面过来,这里有很多地方细节都需要修改,特别是社交登录下方那个是图片才可以实现,我们先增加一下login按钮点击 后跳转到 smslogin.dart

要给 login 按钮增加点击跳转到 smslogin.dart 页面的功能,你需要在 onPressed 回调中使用 Navigator 进行页面跳转,我们需要以下步骤

在注册页面修改登录按钮的代码:

代码语言:javascript
代码运行次数:0
复制
BlackButton(
  label: 'login',
  onPressed: () {
    // 登录按钮点击事件
    logger.info('登录按钮被点击');
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => SmsLoginScreen()),
    );
  },
),

并且增加引入:

代码语言:javascript
代码运行次数:0
复制
import 'package:ff_flutter/screens/smslogin.dart'; // 假设 smslogin.dart 文件位于 screens 目录下

在smslogin.dart页面增加

代码语言:javascript
代码运行次数:0
复制
// smslogin.dart
class SmsLoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SMS Login'),
      ),
      body: Center(
        child: const Text('SMS Login Screen'),
      ),
    );
  }
}

完成后,但是我们得到一个警告

代码语言:javascript
代码运行次数:0
复制
Constructors for public widgets should have a named 'key' parameter.
Try adding a named parameter to the constructor.

大意是

Flutter 建议为公共小部件的构造函数添加一个命名的 key 参数。为了符合这个建议,你需要在 SmsLoginScreen 和 RegisterScreen 的构造函数中添加 Key 参数。

那么扩展知识又来了

扩展知识

在Flutter中,为小部件的构造函数添加一个命名的 key 参数有以下几个主要作用:

1. 唯一标识小部件

每个小部件都可以通过 key 参数在树结构中唯一标识。这在重建部分树时特别有用,因为它有助于Flutter引擎高效地更新和重用小部件,而不是销毁和重建它们。

2. 保持状态

当你需要保持某个状态时(例如在列表中拖动排序项目),key 参数可以确保小部件在重建时保持其状态。例如,在一个可变顺序的列表中,如果每个项目都有唯一的 key,那么在列表项被重新排列时,它们的状态仍能正确保持。

3. 控制小部件重建

key 参数可以帮助Flutter引擎决定是否需要重建小部件。通过比较 key 值,Flutter可以在更新UI时更智能地选择重建哪些部分,从而提高性能。

代码示例

以下是如何为小部件添加一个命名的 key 参数的示例

代码语言:javascript
代码运行次数:0
复制
import 'package:flutter/material.dart';

class CustomWidget extends StatelessWidget {
  final String title;
  const CustomWidget({Key? key, required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(title),
    );
  }
}

在上面的示例中,CustomWidget 有一个可选的 key 参数,并在构造函数中使用 super(key: key) 进行初始化。当你创建这个小部件的实例时,可以传递一个 key

代码语言:javascript
代码运行次数:0
复制
CustomWidget(
  key: ValueKey('unique_key'),
  title: 'My Custom Widget',
)

ok 我们照做,

Key 类型

在Flutter中,有几种不同类型的 Key,你可以根据具体需求选择使用:

ValueKey<T>: 通过值来唯一标识小部件,适用于简单数据类型(如字符串或数字)。

ObjectKey: 通过对象来唯一标识小部件,适用于复杂数据类型。

UniqueKey: 保证每次创建时都唯一,适用于需要绝对唯一性的场景(但不能用于状态保持)。

也就是说,接下来需要在 register_screen.dart 中为 RegisterScreen 添加一个 key 值 reg,在 smslogin.dart 中为 SmsLoginScreen 添加一个 key 值 slogin。

以下是修改后的代码

在顶部加入

代码语言:javascript
代码运行次数:0
复制
const RegisterScreen({Key? key}) : super(key: key); // 添加 key 参数

在注册处:

代码语言:javascript
代码运行次数:0
复制
key: const Key('reg'), // 添加 key 值 'reg'

在登录按钮处:

代码语言:javascript
代码运行次数:0
复制
MaterialPageRoute(builder: (context) => SmsLoginScreen(key: const Key('slogin'))), // 添加 key 值 'slogin'

注册页面我们点击登录成功跳转到了登录页面,成功实现了 跳转,本来打算本篇幅 写完所有注册页面,但是看来过长 需要下一篇了,其次有个大一点的原因是注册逻辑有问题,因此我们先去做其他页面。

完整页面供参考,

注册页面

代码语言:javascript
代码运行次数:0
复制
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:ff_flutter/widgets/pinkbutton.dart'; // 引入 PinkButton
import 'package:ff_flutter/widgets/blackbutton.dart'; // 引入 BlackButton
import 'package:ff_flutter/screens/smslogin.dart'; // 假设 smslogin.dart 文件位于 screens 目录下

class RegisterScreen extends StatefulWidget {
  const RegisterScreen({Key? key}) : super(key: key); // 添加 key 参数


  @override
  State<RegisterScreen> createState() => _RegisterScreenState();
}

class _RegisterScreenState extends State<RegisterScreen> {
  // 示例国家地区号列表
  final List<String> countryCodes = ['+1', '+86', '+91', '+44', '+33'];

  // 默认选择的国家地区号
  String selectedCountryCode = '+1';

  // Checkbox 状态
  bool _agreedToTerms = false;

  @override
  Widget build(BuildContext context) {
    final logger = Logger('RegisterScreen');
    logger.info('Building RegisterScreen');

    return Scaffold(

      key: const Key('reg'), // 添加 key 值 'reg'
      backgroundColor: const Color(0xFF1E1E1E), // 设置背景色为 #1E1E1E
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              "Free Friend",
              style: TextStyle(
                color: Colors.white,
                fontSize: 61.87,
                fontFamily: "PingFang SC",
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 16.0),
            const Text(
              "Please register your account",
              style: TextStyle(
                color: Colors.white,
                fontSize: 32,
                fontFamily: "PingFang SC",
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 16.0),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,  // 使用 spaceBetween 对齐方式
              children: [
                Flexible(
                  flex: 1,  // 给 DropdownButtonFormField 分配一部分空间
                  child: DropdownButtonFormField<String>(
                    value: selectedCountryCode,
                    onChanged: (String? newValue) {
                      if (newValue != null) {
                        setState(() {
                          selectedCountryCode = newValue;
                        });
                      }
                    },
                    items: countryCodes.map<DropdownMenuItem<String>>((String value) {
                      return DropdownMenuItem<String>(
                        value: value,
                        child: Text(
                          value,
                          style: const TextStyle(color: Colors.white), // 设置文字颜色为 FFFFFF
                        ),
                      );
                    }).toList(),
                    decoration: const InputDecoration(
                      labelText: '选择国家地区号',
                      border: OutlineInputBorder(),
                    ),
                    style: const TextStyle(
                      fontSize: 16,
                      color: Colors.white, // 设置文字颜色为 FFFFFF
                    ),
                    dropdownColor: const Color(0xFF1E1E1E), // 设置弹窗背景色为 #1E1E1E
                  ),
                ),
                const SizedBox(width: 8.0),
                const Expanded(
                  flex: 2,  // 给 TextField 分配更多的空间
                  child: TextField(
                    decoration: InputDecoration(
                      labelText: '请输入手机号',
                      border: OutlineInputBorder(),
                      hintStyle: TextStyle(color: Color(0xffa9a9a9)),
                    ),
                    style: TextStyle(color: Colors.white), // 设置输入文字颜色为 FFFFFF
                    keyboardType: TextInputType.phone,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 16.0),
            const TextField(
              decoration: InputDecoration(
                labelText: '请输入密码',
                hintStyle: TextStyle(color: Color(0xffa9a9a9)),
                border: OutlineInputBorder(),
              ),
              obscureText: true,
              style: TextStyle(color: Colors.white), // 设置输入文字颜色为 FFFFFF
            ),
            Row(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Checkbox(
                  value: _agreedToTerms,
                  onChanged: (bool? value) {
                    setState(() {
                      _agreedToTerms = value ?? false;
                    });
                  },
                ),
                const SizedBox(width: 20),
                const Text(
                  "You agree to our Terms",
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 32,
                    fontFamily: "PingFang SC",
                    fontWeight: FontWeight.w500,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 24.0),
            PinkButton(
              label: 'Register',
              onPressed: () {
                // 注册按钮点击事件
                logger.info('注册按钮被点击');
              },
            ),
            const SizedBox(height: 8.0),
            Expanded(
              child: Align(
                alignment: Alignment.bottomCenter,
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const Text(
                      "Already have an account?",
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 32,
                        fontFamily: "PingFang SC",
                        fontWeight: FontWeight.w800,
                      ),
                    ),
                    const SizedBox(height: 8.0),
                  BlackButton(
  label: 'login',
  onPressed: () {
    // 登录按钮点击事件
    logger.info('登录按钮被点击');
    Navigator.push(
      context,
       MaterialPageRoute(builder: (context) => SmsLoginScreen(key: const Key('slogin'))), // 添加 key 值 'slogin'
    );
  },
),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

登录页面

代码语言:javascript
代码运行次数:0
复制
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:ff_flutter/widgets/pinkbutton.dart'; // 引入 PinkButton
import 'package:ff_flutter/widgets/blackbutton.dart'; // 引入 BlackButton





// smslogin.dart
class SmsLoginScreen extends StatelessWidget {
  const SmsLoginScreen({Key? key}) : super(key: key); // 添加 key 参数



  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SMS Login'),
      ),
      body: Center(
        child: const Text('SMS Login Screen'),
      ),
    );
  }































}

class RegisterScreen extends StatefulWidget {
  const RegisterScreen({super.key});

  @override
  State<RegisterScreen> createState() => _RegisterScreenState();
}

class _RegisterScreenState extends State<RegisterScreen> {
  // 示例国家地区号列表
  final List<String> countryCodes = ['+1', '+86', '+91', '+44', '+33'];

  // 默认选择的国家地区号
  String selectedCountryCode = '+1';

  // Checkbox 状态
  bool _agreedToTerms = false;

  @override
  Widget build(BuildContext context) {
    final logger = Logger('RegisterScreen');
    logger.info('Building RegisterScreen');

    return Scaffold(
      backgroundColor: const Color(0xFF1E1E1E), // 设置背景色为 #1E1E1E
      // appBar: AppBar(
      //   title: const Text(
      //     'Free Friend',
      //     style: TextStyle(
      //       fontSize: 24.0, // 设置字体大小
      //       fontFamily: 'PingFang SC', // 设置字体为 PingFang SC
      //     ),
      //   ),
      //   centerTitle: true, // 居中标题
      // ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              "Free Friend",
              style: TextStyle(
                color: Colors.white,
                fontSize: 61.87,
                fontFamily: "PingFang SC",
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 16.0),
            const Text(
              "Please login your account",
              style: TextStyle(
                color: Colors.white,
                fontSize: 32,
                fontFamily: "PingFang SC",
                fontWeight: FontWeight.w800,
              ),
            ),
            const SizedBox(height: 16.0),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,  // 使用 spaceBetween 对齐方式
              children: [
                Flexible(
                  flex: 1,  // 给 DropdownButtonFormField 分配一部分空间
                  child: DropdownButtonFormField<String>(
                    value: selectedCountryCode,
                    onChanged: (String? newValue) {
                      if (newValue != null) {
                        setState(() {
                          selectedCountryCode = newValue;
                        });
                      }
                    },
                    items: countryCodes.map<DropdownMenuItem<String>>((String value) {
                      return DropdownMenuItem<String>(
                        value: value,
                        child: Text(
                          value,
                          style: const TextStyle(color: Colors.white), // 设置文字颜色为 FFFFFF
                        ),
                      );
                    }).toList(),
                    decoration: const InputDecoration(
                      labelText: '选择国家地区号',
                      border: OutlineInputBorder(),
                    ),
                    style: const TextStyle(
                      fontSize: 16,
                      color: Colors.white, // 设置文字颜色为 FFFFFF
                    ),
                    dropdownColor: const Color(0xFF1E1E1E), // 设置弹窗背景色为 #1E1E1E
                  ),
                ),
                const SizedBox(width: 8.0),
                const Expanded(
                  flex: 2,  // 给 TextField 分配更多的空间
                  child: TextField(
                    decoration: InputDecoration(
                      labelText: '请输入手机号',
                      border: OutlineInputBorder(),
                      hintStyle: TextStyle(color: Color(0xffa9a9a9)),
                    ),
                    style: TextStyle(color: Colors.white), // 设置输入文字颜色为 FFFFFF
                    keyboardType: TextInputType.phone,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 16.0),
            const TextField(
              decoration: InputDecoration(
                labelText: '请输入密码',
                hintStyle: TextStyle(color: Color(0xffa9a9a9)),
                border: OutlineInputBorder(),
              ),
              obscureText: true,
              style: TextStyle(color: Colors.white), // 设置输入文字颜色为 FFFFFF
            ),
            Row(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Checkbox(
                  value: _agreedToTerms,
                  onChanged: (bool? value) {
                    setState(() {
                      _agreedToTerms = value ?? false;
                    });
                  },
                ),
                const SizedBox(width: 20),
                const Text(
                  "You agree to our Terms",
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 32,
                    fontFamily: "PingFang SC",
                    fontWeight: FontWeight.w500,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 24.0),
            PinkButton(
              label: 'Register',
              onPressed: () {
                // 注册按钮点击事件
                logger.info('注册按钮被点击');
              },
            ),
            const SizedBox(height: 8.0),
            Expanded(
              child: Align(
                alignment: Alignment.bottomCenter,
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const Text(
                      "Already have an account?",
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 32,
                        fontFamily: "PingFang SC",
                        fontWeight: FontWeight.w800,
                      ),
                    ),
                    const SizedBox(height: 8.0),
                    BlackButton(
                      label: 'login',
                      onPressed: () {
                        // 登录按钮点击事件
                        logger.info('登录按钮被点击');
                      },
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

其次我改了下名字,register_screen.dart改为register.dart,其次打包了apk 供下载给大家看。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 章节内容【06】
  • 开发背景
  • demo下载
  • 实战开始
    • 1. 唯一标识小部件
    • 2. 保持状态
    • 3. 控制小部件重建
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档