用flutter编写一个个人信息页

https://api.flutter.dev/flutter/material/Scaffold-class.html

image-20241004200455120

完整代码

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo_app/src/providers/userinfoProvider.dart';

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

  @override
  State<PersonalPageDemo> createState() => _PersonalPageDemoState();
}

class _PersonalPageDemoState extends State<PersonalPageDemo> {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController =
      TextEditingController(text: "*************");
  final TextEditingController _birthdayController = TextEditingController();
  String? _selectedGender;
  bool _isEditing = false; // 控制是否处于编辑模式
  @override
  void initState() {
    super.initState();
    _loadUserInfo();
  }

  void _loadUserInfo() async {
    final userInfoProvider =
        Provider.of<UserInfoProvider>(context, listen: false);
    await userInfoProvider.fetchUserInfoProvider();
    setState(() {
      _emailController.text = userInfoProvider.userInfo!.email;
      _phoneController.text = userInfoProvider.userInfo!.phoneNumber;
      _usernameController.text = userInfoProvider.userInfo!.username;
      _birthdayController.text = userInfoProvider.userInfo!.birthday;
      _selectedGender = userInfoProvider.userInfo!.gender;
    });
  }

  void _saveUserInfo() {
    //数据库操作

    setState(() {
      _isEditing = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    final userInfoProvider = Provider.of<UserInfoProvider>(context);
    final userInfo = userInfoProvider.userInfo;
    final username = userInfo?.username ?? "";
    final avatar = userInfo?.avatar ?? "";
    final bio = userInfo?.bio ?? "";
    return Scaffold(
      appBar: AppBar(
        title: const Text("个人主页"),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Center(
            child: Column(
              children: [
                CircleAvatar(
                  radius: 50,
                  backgroundImage: NetworkImage(avatar),
                ),
                const SizedBox(height: 10),
                Text(
                  username,
                  style: const TextStyle(
                    fontSize: 24,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                Text(bio,
                    style: const TextStyle(
                        fontSize: 15, fontWeight: FontWeight.normal)),
                // 添加边距
                const SizedBox(height: 20),
                _buildTextInputField(
                    label: "用户名:", controller: _usernameController),
                const SizedBox(height: 10),
                _buildTextInputField(
                    label: "密码:",
                    controller: _passwordController,
                    isPassword: true),
                const SizedBox(height: 10),
                _buildTextInputField(
                    label: "邮箱:", controller: _emailController),
                const SizedBox(height: 10),
                _buildGenderSelector(),
                const SizedBox(
                  height: 20,
                ),
                _buildTextInputField(
                    label: "电话:", controller: _phoneController),
                const SizedBox(height: 10),
                _buildTextInputField(
                    label: "生日:", controller: _birthdayController),
                const SizedBox(height: 20),
                // 添加 编辑 & 保存按钮
                SizedBox(
                  width: double.infinity, // 使按钮占据整个宽度
                  child: ElevatedButton(
                    onPressed: _isEditing
                        ? _saveUserInfo
                        : () {
                            setState(() {
                              _isEditing = true; // 切换到编辑模式
                            });
                          },
                    child: Text(_isEditing ? '保存' : '编辑'),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildTextInputField({
    required String label,
    required TextEditingController controller,
    bool isPassword = false,
  }) {
    return Row(
      children: [
        Text(
          '$label ',
          style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
        ),
        // TextField 文字框
        Expanded(
          child: TextField(
            controller: controller,
            enabled: _isEditing,
            obscureText: isPassword, // 如果是密码,输入新密码的时候会变成*号
            style: const TextStyle(fontSize: 18),
          ),
        ),
      ],
    );
  }

  // 创建性别选择器
  Widget _buildGenderSelector() {
    return Row(
      children: [
        const Text(
          '性别: ',
          style: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.bold,
          ),
        ),
        Expanded(
          child: GestureDetector(
            onTap: () async {
              if (_isEditing) {
                final selectedGender = await showDialog<String>(
                  context: context,
                  builder: (BuildContext context) {
                    return SimpleDialog(
                      title: const Text('选择性别'),
                      children: ['男', '女'].map((String gender) {
                        return SimpleDialogOption(
                          onPressed: () {
                            Navigator.pop(context, gender);
                          },
                          child: Text(gender),
                        );
                      }).toList(),
                    );
                  },
                );
                if (selectedGender != null) {
                  setState(() {
                    _selectedGender = selectedGender;
                  });
                }
              }
            },
            child: AbsorbPointer(
              child: TextField(
                controller: TextEditingController(text: _selectedGender),
                enabled: _isEditing, // 始终禁用,点击通过 GestureDetector 控制
                decoration: const InputDecoration(
                  suffixIcon: Icon(Icons.arrow_drop_down),
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

下面我们分段解释代码

我们先创建一个stateful组件 PersonalProfile

然后在_PersonalProfileState类里面编写

  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController =
      TextEditingController(text: "*************");
  final TextEditingController _birthdayController = TextEditingController();
  String? _selectedGender;
  bool _isEditing = false; // 控制是否处于编辑模式

这些分别为输入框TextField的controller

由变量名我们可以推断出,分别为邮箱输入框的controller,手机号输入框的controller,用户名输入框的controller,密码输入框的controller,生日输入框的controller

然后我们需要一个String? _selectedGender,后续我们使用showDialog中的Navigator.pop(context, gender) 会将所选的性别(gender)作为结果返回,存储在_selectedGender中,方便我们后面把相关信息存储到数据库中

bool _isEditing = false; // 控制是否处于编辑模式

然后我们在build函数中return一个骨架屏

image-20241004190448144

import 'package:flutter/material.dart';

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

  @override
  State<PersonalPageDemo> createState() => _PersonalPageDemoState();
}

class _PersonalPageDemoState extends State<PersonalPageDemo> {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController =
      TextEditingController(text: "*************");
  final TextEditingController _birthdayController = TextEditingController();
  String? _selectedGender;
  bool _isEditing = false; // 控制是否处于编辑模式
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      
    );
  }
}

我们添加一个appBar

import 'package:flutter/material.dart';

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

  @override
  State<PersonalPageDemo> createState() => _PersonalPageDemoState();
}

class _PersonalPageDemoState extends State<PersonalPageDemo> {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController =
      TextEditingController(text: "*************");
  final TextEditingController _birthdayController = TextEditingController();
  String? _selectedGender;
  bool _isEditing = false; // 控制是否处于编辑模式
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("个人主页"),
      ),
    );
  }
}

我们把body设置为SingleChildScrollView,当我们后续添加很多input框后可能会超出设备的高度,使用这个我们就能滑动了

再加一个Padding,对周边设置边距为20,它包括一个child,里面可以放我们剩下的元素。

child是一个Column列,里面有一个children属性,可以放很多元素,我们就要在这里正式编写我们的页面了

头像和个性签名

import 'package:flutter/material.dart';

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

  @override
  State<PersonalPageDemo> createState() => _PersonalPageDemoState();
}

class _PersonalPageDemoState extends State<PersonalPageDemo> {
  final TextEditingController _emailController =
      TextEditingController(text: "example@example.com");
  final TextEditingController _phoneController =
      TextEditingController(text: "19988887744");
  final TextEditingController _usernameController =
      TextEditingController(text: "MeowRain");
  final TextEditingController _passwordController =
      TextEditingController(text: "*************");
  final TextEditingController _birthdayController =
      TextEditingController(text: "2000-11-11");
  String? _selectedGender;
  bool _isEditing = false; // 控制是否处于编辑模式

  @override
  Widget build(BuildContext context) {
    String avatar =
        "https://meowrain.cn/upload/2024/06/IMG_20240511_124707_183.jpg";
    return Scaffold(
      appBar: AppBar(
        title: const Text("个人主页"),
      ),
      body: SingleChildScrollView(
        child: Padding(
            padding: const EdgeInsets.all(20.0),
            child: Center(
              child: Column(
                children: [
                  CircleAvatar(
                    radius: 50,
                    backgroundImage: NetworkImage(avatar),
                  ),
                  const SizedBox(height: 10),
                  Text(
                    "MeowRain",
                    style: const TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Text("😊Hi,there",
                      style: const TextStyle(
                          fontSize: 15, fontWeight: FontWeight.normal))
                ],
              ),
            )),
      ),
    );
  }
}

image-20241004192454684

输入框信息

因为接下来我们要创建好几个textfield,为了不让代码变得冗杂,我们创建一个函数组件_buildTextField,返回一个Widget

  Widget _buildTextInputField(
      {required String label,
      required TextEditingController controller,
      isPassword = false}) {
    return Row(
      children: [
        Text('$label ',
            style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),

        //TextField 文字框
          //Expanded是为了输入框能占满横的距离
        Expanded(
            child: TextField(
          controller: controller,
          enabled: _isEditing,
          obscureText: isPassword, //如果是密码,输入新密码的时候会变成*号
          style: const TextStyle(fontSize: 18),
        ))
      ],
    );
  }

然后我们就能在build函数中使用这个组件了

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo_app/src/providers/userinfoProvider.dart';

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

  @override
  State<PersonalPageDemo> createState() => _PersonalPageDemoState();
}

class _PersonalPageDemoState extends State<PersonalPageDemo> {
  final TextEditingController _emailController =
      TextEditingController(text: "example@example.com");
  final TextEditingController _phoneController =
      TextEditingController(text: "19988887744");
  final TextEditingController _usernameController =
      TextEditingController(text: "MeowRain");
  final TextEditingController _passwordController =
      TextEditingController(text: "*************");
  final TextEditingController _birthdayController =
      TextEditingController(text: "2000-11-11");
  String? _selectedGender;
  bool _isEditing = false; // 控制是否处于编辑模式

  @override
  Widget build(BuildContext context) {
    String avatar =
        "https://meowrain.cn/upload/2024/06/IMG_20240511_124707_183.jpg";
    return Scaffold(
      appBar: AppBar(
        title: const Text("个人主页"),
      ),
      body: SingleChildScrollView(
        child: Padding(
            padding: const EdgeInsets.all(20.0),
            child: Center(
              child: Column(
                children: [
                  CircleAvatar(
                    radius: 50,
                    backgroundImage: NetworkImage(avatar),
                  ),
                  const SizedBox(height: 10),
                  const Text(
                    "MeowRain",
                    style: const TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const Text("😊Hi,there",
                      style: const TextStyle(
                          fontSize: 15, fontWeight: FontWeight.normal)),
                  // 添加边距
                  const SizedBox(
                    height: 20,
                  ),
                  _buildTextInputField(
                      label: "用户名:", controller: _usernameController),
                  const SizedBox(
                    height: 10,
                  ),
                  _buildTextInputField(
                      label: "密码:",
                      controller: _passwordController,
                      isPassword: true),
                  const SizedBox(
                    height: 10,
                  ),
                  _buildTextInputField(
                      label: "邮箱:", controller: _emailController),
                  const SizedBox(
                    height: 10,
                  ),
                  _buildTextInputField(
                      label: "电话:", controller: _phoneController),
                  const SizedBox(
                    height: 10,
                  ),
                  _buildTextInputField(
                      label: "生日:", controller: _birthdayController)
                ],
              ),
            )),
      ),
    );
  }

  Widget _buildTextInputField(
      {required String label,
      required TextEditingController controller,
      isPassword = false}) {
    return Row(
      children: [
        Text('$label ',
            style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),

        //TextField 文字框
        Expanded(
            child: TextField(
          controller: controller,
          enabled: _isEditing,
          obscureText: isPassword, //如果是密码,输入新密码的时候会变成*号
          style: const TextStyle(fontSize: 18),
        ))
      ],
    );
  }
}

image-20241004193721968

编辑&保存按钮

我们需要一个按钮,它初始显示编辑,当我们点击它的时候,可以把_isEditing设置为true,表示启动了编辑模式,同时按钮中的文字变成保存,上面的输入框变成可编辑状态,

当我们再次点击保存的时候,会把新的状态设置到上面的变量中,也就是设置controller.text,同时执行保存到数据库的操作

我们使用ElevatedButton

为了让按钮能占据横的全部长度,我们用SizedBox包起来

                SizedBox(
                  width: double.infinity, // 使按钮占据整个宽度
                  child: ElevatedButton(
                    onPressed: _isEditing
                        ? _saveUserInfo
                        : () {
                            setState(() {
                              _isEditing = true; // 如果不是编辑模式,那么点击就要设置为编辑模式
                            });
                          },
                    child: Text(_isEditing ? '保存' : '编辑'),
                  ),
                ),

_saveUserInfo

  void _saveUserInfo() {
    setState(() {
      _isEditing = false;
    });
  }

目前完整代码:

import 'package:flutter/material.dart';

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

  @override
  State<PersonalPageDemo> createState() => _PersonalPageDemoState();
}

class _PersonalPageDemoState extends State<PersonalPageDemo> {
  final TextEditingController _emailController =
      TextEditingController(text: "example@example.com");
  final TextEditingController _phoneController =
      TextEditingController(text: "19988887744");
  final TextEditingController _usernameController =
      TextEditingController(text: "MeowRain");
  final TextEditingController _passwordController =
      TextEditingController(text: "*************");
  final TextEditingController _birthdayController =
      TextEditingController(text: "2000-11-11");
  String? _selectedGender;
  bool _isEditing = false; // 控制是否处于编辑模式

  void _saveUserInfo() {
    setState(() {
      _isEditing = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    String avatar =
        "https://meowrain.cn/upload/2024/06/IMG_20240511_124707_183.jpg";
    return Scaffold(
      appBar: AppBar(
        title: const Text("个人主页"),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Center(
            child: Column(
              children: [
                CircleAvatar(
                  radius: 50,
                  backgroundImage: NetworkImage(avatar),
                ),
                const SizedBox(height: 10),
                const Text(
                  "MeowRain",
                  style: TextStyle(
                    fontSize: 24,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const Text("😊Hi,there",
                    style:
                        TextStyle(fontSize: 15, fontWeight: FontWeight.normal)),
                // 添加边距
                const SizedBox(height: 20),
                _buildTextInputField(
                    label: "用户名:", controller: _usernameController),
                const SizedBox(height: 10),
                _buildTextInputField(
                    label: "密码:",
                    controller: _passwordController,
                    isPassword: true),
                const SizedBox(height: 10),
                _buildTextInputField(
                    label: "邮箱:", controller: _emailController),
                const SizedBox(height: 10),
                _buildTextInputField(
                    label: "电话:", controller: _phoneController),
                const SizedBox(height: 10),
                _buildTextInputField(
                    label: "生日:", controller: _birthdayController),
                const SizedBox(height: 20),
                // 添加 编辑 & 保存按钮
                SizedBox(
                  width: double.infinity, // 使按钮占据整个宽度
                  child: ElevatedButton(
                    onPressed: _isEditing
                        ? _saveUserInfo
                        : () {
                            setState(() {
                              _isEditing = true; // 切换到编辑模式
                            });
                          },
                    child: Text(_isEditing ? '保存' : '编辑'),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildTextInputField({
    required String label,
    required TextEditingController controller,
    bool isPassword = false,
  }) {
    return Row(
      children: [
        Text(
          '$label ',
          style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
        ),
        // TextField 文字框
        Expanded(
          child: TextField(
            controller: controller,
            enabled: _isEditing,
            obscureText: isPassword, // 如果是密码,输入新密码的时候会变成*号
            style: const TextStyle(fontSize: 18),
          ),
        ),
      ],
    );
  }
}

image-20241004195939861

image-20241004195956971