flutter开发环境搭建略
如果是新搭建好的环境,需要开启web支持和windows支持
flutter config --enable-windows-desktop
flutter config --enable-web
创建项目名为spore,英文翻译为孢子,希望可以”做大做强“(无名之辈的发音)吧,毕竟谁不好大喜功呢!
flutter create spore
cd spore
web运行查看
flutter run -d Chrome
windows运行查看
flutter run -d Windows
本篇的主要目的就是把这个页面搞成登录页,就算OK
来说一下目录结构
红色箭头指向的两个文件夹是添加平台支持后才会有的,如果你的目录中没有,就是添加支持出问题,或者干脆忘记添加了。
蓝色箭头指向的是和本篇关系很大的两个文件,尤其是main.dart,是整个项目的主入口文件,灰常重要
因为是第一篇,就搞的尽量简单点,后面丰富吧,我一向提倡先解决有的问题,再解决好看的问题。当然也不能太不想个样子,lib下新建文件夹pages,pages新建login.dart
先贴代码的基本结构,注释说明
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {//继承StatefulWidget类,表示它是一个有状态的组件
const LoginPage({Key? key}) : super(key: key);
@override
_LoginPageState createState() => _LoginPageState();//如果集成StatelessWidget类,这里就该是Widget build(BuildContext context)
}
class _LoginPageState extends State<LoginPage>{
@override
Widget build(BuildContext context) {//构建UI,写控件的地方
// TODO: implement build
throw UnimplementedError();
}
}
flutter把控件从状态的角度分为两大类:StatefulWidget和StatelessWidget,据说懂RN的这个自然懂,反正我不懂。。。
StatefulWidget为了更好的提升实现状态管理和UI重绘等性能,设计成createState()返回一个组件的State对象来进行操作。具体原理先不用懂,反正就是王八的屁股—规定
基础勉强实现功能版
代码如下
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {//继承StatefulWidget类,表示它是一个有状态的组件
const LoginPage({Key? key}) : super(key: key);
@override
_LoginPageState createState() => _LoginPageState();//如果集成StatelessWidget类,这里就该是Widget build(BuildContext context)
}
class _LoginPageState extends State<LoginPage>{
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
String? userName;
String? password;
@override
Widget build(BuildContext context) {//构建UI,写控件的地方
return Theme(
data: ThemeData(),
child: Scaffold(
body: _buildPageContent(),
),
);
}
Widget _buildPageContent() {
return Container(
color: const Color.fromARGB(255, 148, 200, 207),
child: ListView(
children: <Widget>[
const Center(
child: Text(
"欢迎使用本系统",
style: TextStyle(
fontSize: 18,
color: Colors.black
),
textScaleFactor: 3.2 //缩放倍数,没有这个字会很小
)
),
const SizedBox(height: 20.0),
_buildLoginForm(),//缩进写到烦人,搞个方法。
const SizedBox(height: 20.0),
],
),
);
}
Container _buildLoginForm() {
return Container(
color: const Color.fromARGB(255, 45, 183, 201),
child: Stack(//自由位置的组件
children: [
Center(
child: Container(
width: 500,
height: 260,
margin: EdgeInsets.only(top: 40),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(//白色的圆角方框
borderRadius: BorderRadius.all(Radius.circular(40.0)),
color: Colors.white,
),
child: Form(
key: formKey,
child: Column(
children: [
Container(
child: TextFormField(
initialValue: "admin",//可以不要这个参数
onSaved: (v) {
userName = v;
},
validator: (v) {
return v!.isEmpty ? "请输入用户名" : null;
},
),
),
Container(
child: TextFormField(
initialValue: "123456",
obscureText: true,
onSaved: (v) {
password = v;
},
validator: (v) {
return v!.isEmpty ? "请输入密码" : null;
},
),
),
],
)
)
),
),
Container(
height: 260,
alignment: Alignment.bottomCenter,
child: SizedBox(
width: 420,
child: ElevatedButton(
onPressed: _login,
style: ButtonStyle(
shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(40.0))),
),
child: const Text("登录", style: TextStyle(color: Colors.white70, fontSize: 20)),
),
),
),
],
),
);
}
_login(){
var form = formKey.currentState!;
if (!form.validate()) {
return;
}
form.save();//会调用TextFormField的onSave
if(userName=='admin' && password=='123456'){
_showAlertDialog(context,'登录成功');
}else{
_showAlertDialog(context,'用户名或密码错误');
}
}
//没有找到方便的toast控件,搞个这代替
_showAlertDialog(BuildContext context,String msg) {
//设置按钮
Widget okButton = TextButton(
child: const Text("OK"),
onPressed: () {Navigator.of(context).pop(); },
);
//设置对话框
AlertDialog alert = AlertDialog(
title: const Text("提示"),
content: Text(msg),
actions: [
okButton,
],
);
//显示对话框
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
}
精细化版,抄别人的,最后贴地址
虽然还是丑,以后有的是机会再优化,代码如下:
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {//继承StatefulWidget类,表示它是一个有状态的组件
const LoginPage({Key? key}) : super(key: key);
@override
_LoginPageState createState() => _LoginPageState();//如果集成StatelessWidget类,这里就该是Widget build(BuildContext context)
}
class _LoginPageState extends State<LoginPage>{
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
String? userName;
String? password;
FocusNode focusNodeUserName = FocusNode();
FocusNode focusNodePassword = FocusNode();
@override
Widget build(BuildContext context) {//构建UI,写控件的地方
return Theme(
data: ThemeData(),
child: Scaffold(
body: _buildPageContent(),
),
);
}
Widget _buildPageContent() {
return Container(
color: const Color.fromARGB(255, 110, 238, 255),
child: ListView(
children: <Widget>[
const Center(
child: Text(
"欢迎使用本系统",
style: TextStyle(
fontSize: 18,
color: Colors.black
),
textScaleFactor: 3.2 //缩放倍数,没有这个字会很小
)
),
const SizedBox(height: 20.0),
_buildLoginForm(),//缩进写到烦人,搞个方法。
const SizedBox(height: 20.0),
],
),
);
}
Container _buildLoginForm() {
return Container(
padding: const EdgeInsets.fromLTRB(0, 30, 0, 30),
color: const Color.fromARGB(255, 45, 183, 201),
child: Stack(//自由位置的组件
children: [
Center(
child: Container(
width: 500,
height: 320,
margin: const EdgeInsets.only(top: 40),
padding: const EdgeInsets.all(10.0),
decoration: const BoxDecoration(//白色的圆角方框
borderRadius: BorderRadius.all(Radius.circular(40.0)),
color: Colors.white,
),
child: Form(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,//垂直居中
children: [
Container(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
child: TextFormField(
focusNode: focusNodeUserName,
initialValue: "admin",
style: const TextStyle(color: Colors.black),
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: "用户名",
icon: Icon(
Icons.people,
color: Colors.blue,
),
),
onSaved: (v) {
userName = v;
},
validator: (v) {
return v!.isEmpty ? "请输入用户名" : null;
},
onFieldSubmitted: (v) {//按shift触发方法
focusNodePassword.requestFocus();
},
),
),
Container(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
child: TextFormField(
focusNode: focusNodePassword,
initialValue: "123456",
obscureText: true,
style: const TextStyle(color: Colors.black),
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: "密码",
icon: Icon(
Icons.lock,
color: Colors.blue,
),
),
onSaved: (v) {
password = v;
},
validator: (v) {
return v!.isEmpty ? "请输入密码" : null;
},
onFieldSubmitted: (v) {
_login();
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextButton(
child: const Text(
'去注册',
style: TextStyle(color: Colors.blue),
),
onPressed: () {},
),
TextButton(
child: const Text(
'忘记密码',
style: TextStyle(color: Colors.black45),
),
onPressed: () {},
)
],
),
],
)
)
),
),
Container(
height: 320,
alignment: Alignment.bottomCenter,
child: SizedBox(
width: 420,
child: ElevatedButton(
onPressed: _login,
style: ButtonStyle(
shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(40.0))),
),
child: const Text("登录", style: TextStyle(color: Colors.white70, fontSize: 20)),
),
),
),
],
),
);
}
_login(){
var form = formKey.currentState!;
if (!form.validate()) {
return;
}
form.save();//会调用TextFormField的onSave
//等集成dio再优化
if(userName=='admin' && password=='123456'){
_showAlertDialog(context,'登录成功');
}else{
_showAlertDialog(context,'用户名或密码错误');
}
}
//没有找到方便的toast控件,搞个这代替
_showAlertDialog(BuildContext context,String msg) {
//设置按钮
Widget okButton = TextButton(
child: const Text("OK"),
onPressed: () {Navigator.of(context).pop(); },
);
//设置对话框
AlertDialog alert = AlertDialog(
title: const Text("提示"),
content: Text(msg),
actions: [
okButton,
],
);
//显示对话框
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
}
注册页面和登录差不多,举一反三尝试复制一份改个名字,稍作调整,就可以了
我并不是精通flutter的移动开发之后写这个博客,是拿别人的项目一边抄,一边学习,一边摸索,一边实践,顺便记录下来,我直接参考的项目地址就列出来,如果有和我想法差不多的,我建议直接拿去学习研究吧
github地址:flutterAdmin gitee也有,但是作者好久没更新了,我就不贴了
另一个 知乎看到的另一个看着也不错的,可以看看,作参考
本篇博客完整demo下载spore-01.zip