用flutter编写一个个人信息页
https://api.flutter.dev/flutter/material/Scaffold-class.html
完整代码
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一个骨架屏
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))
],
),
)),
),
);
}
}
输入框信息
因为接下来我们要创建好几个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),
))
],
);
}
}
编辑&保存按钮
我们需要一个按钮,它初始显示编辑
,当我们点击它的时候,可以把_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),
),
),
],
);
}
}