完成基础学习
@ -41,3 +41,11 @@ samples, guidance on mobile development, and a full API reference.
|
|||||||
- 写布局前,先把结构列好,用函数拆分,用_bulid开头,表示私有布局函数
|
- 写布局前,先把结构列好,用函数拆分,用_bulid开头,表示私有布局函数
|
||||||
- 在使用elevatedbutton时,使用style将样式还原,在外面套一层Container作为装饰,更方便配置
|
- 在使用elevatedbutton时,使用style将样式还原,在外面套一层Container作为装饰,更方便配置
|
||||||
- 使用Navigator.pushReplacement 替换路由
|
- 使用Navigator.pushReplacement 替换路由
|
||||||
|
## 表单
|
||||||
|
- 表单TextField通过textfield.decoration进行装饰
|
||||||
|
- 使用prefixIcon、suffixIcon设置前置、后置图标
|
||||||
|
- 一些简单的验证可以放在TextField的onChanged中
|
||||||
|
- 注意在涉及到更新状态时,使用setState
|
||||||
|
## 按钮组件
|
||||||
|
- 抽取公共组件,放在commont里
|
||||||
|
- 公共组件外露的属性,使用final修饰,并在构造函数中
|
||||||
@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=file:///C:/Users/24811/.gradle/wrapper/dists/gradle-6.7-all.zip
|
# distributionUrl=file:///C:/Users/24811/.gradle/wrapper/dists/gradle-6.7-all.zip
|
||||||
# distributionUrl=file:///D:/Gradle/.gradle/wrapper/dists/gradle-6.7-all.zip
|
distributionUrl=file:///D:/Gradle/.gradle/wrapper/dists/gradle-6.7-all.zip
|
||||||
BIN
assets/images/2.0x/icon_favourite.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/images/2.0x/icon_lock.png
Normal file
|
After Width: | Height: | Size: 860 B |
BIN
assets/images/2.0x/icon_map.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/images/2.0x/icon_offers.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/2.0x/icon_order.png
Normal file
|
After Width: | Height: | Size: 621 B |
BIN
assets/images/2.0x/icon_payment.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/images/2.0x/icon_user.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/images/3.0x/icon_favourite.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
assets/images/3.0x/icon_lock.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/images/3.0x/icon_map.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/images/3.0x/icon_offers.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/images/3.0x/icon_order.png
Normal file
|
After Width: | Height: | Size: 858 B |
BIN
assets/images/3.0x/icon_payment.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
assets/images/3.0x/icon_user.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
@ -1,2 +1,9 @@
|
|||||||
|
static const iconFavouritePng = 'assets/images/icon_favourite.png';
|
||||||
|
static const iconLockPng = 'assets/images/icon_lock.png';
|
||||||
|
static const iconMapPng = 'assets/images/icon_map.png';
|
||||||
|
static const iconOffersPng = 'assets/images/icon_offers.png';
|
||||||
|
static const iconOrderPng = 'assets/images/icon_order.png';
|
||||||
|
static const iconPaymentPng = 'assets/images/icon_payment.png';
|
||||||
|
static const iconUserPng = 'assets/images/icon_user.png';
|
||||||
static const logoPng = 'assets/images/logo.png';
|
static const logoPng = 'assets/images/logo.png';
|
||||||
static const welcomePng = 'assets/images/welcome.png';
|
static const welcomePng = 'assets/images/welcome.png';
|
||||||
|
|||||||
BIN
assets/images/icon_favourite.png
Normal file
|
After Width: | Height: | Size: 723 B |
BIN
assets/images/icon_lock.png
Normal file
|
After Width: | Height: | Size: 376 B |
BIN
assets/images/icon_map.png
Normal file
|
After Width: | Height: | Size: 734 B |
BIN
assets/images/icon_offers.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
assets/images/icon_order.png
Normal file
|
After Width: | Height: | Size: 329 B |
BIN
assets/images/icon_payment.png
Normal file
|
After Width: | Height: | Size: 709 B |
BIN
assets/images/icon_user.png
Normal file
|
After Width: | Height: | Size: 646 B |
@ -1,4 +1,12 @@
|
|||||||
class AssetsImages {
|
class AssetsImages {
|
||||||
static const logoPng = 'assets/images/logo.png';
|
static const iconFavouritePng = 'assets/images/icon_favourite.png';
|
||||||
static const welcomePng = 'assets/images/welcome.png';
|
static const iconLockPng = 'assets/images/icon_lock.png';
|
||||||
|
static const iconMapPng = 'assets/images/icon_map.png';
|
||||||
|
static const iconOffersPng = 'assets/images/icon_offers.png';
|
||||||
|
static const iconOrderPng = 'assets/images/icon_order.png';
|
||||||
|
static const iconPaymentPng = 'assets/images/icon_payment.png';
|
||||||
|
static const iconUserPng = 'assets/images/icon_user.png';
|
||||||
|
static const logoPng = 'assets/images/logo.png';
|
||||||
|
static const welcomePng = 'assets/images/welcome.png';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
42
lib/common/button copy.dart
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ButtonWidget extends StatelessWidget {
|
||||||
|
const ButtonWidget({
|
||||||
|
Key? key, this.text, this.width, this.height, this.radius, this.onPressed,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String? text;
|
||||||
|
final double? width;
|
||||||
|
final double? height;
|
||||||
|
final double? radius;
|
||||||
|
final void Function()? onPressed;
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
//套一层Container,可以做很多事,比如设置尺寸
|
||||||
|
return Container(
|
||||||
|
//不写默认撑开
|
||||||
|
height: height ?? double.infinity,
|
||||||
|
width: width ?? double.infinity,
|
||||||
|
//需要裁切,不然没有圆角
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(radius ?? 32),
|
||||||
|
),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
style: ButtonStyle(
|
||||||
|
elevation: MaterialStateProperty.all(0),
|
||||||
|
//设置最小值为0,是的尺寸为children尺寸
|
||||||
|
minimumSize: MaterialStateProperty.all(Size.zero)),
|
||||||
|
child: Text(text ?? "",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w300,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
lib/common/button.dart
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ButtonWidget extends StatelessWidget {
|
||||||
|
const ButtonWidget({
|
||||||
|
Key? key,
|
||||||
|
this.text,
|
||||||
|
this.width,
|
||||||
|
this.height,
|
||||||
|
this.radius,
|
||||||
|
this.onPressed,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String? text;
|
||||||
|
final double? width;
|
||||||
|
final double? height;
|
||||||
|
final double? radius;
|
||||||
|
final void Function()? onPressed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
//套一层Container,可以做很多事,比如设置尺寸
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
style: ButtonStyle(
|
||||||
|
elevation: MaterialStateProperty.all(0),
|
||||||
|
//设置最小值为0,是的尺寸为children尺寸
|
||||||
|
minimumSize: MaterialStateProperty.all(Size(
|
||||||
|
width ?? double.infinity,
|
||||||
|
height ?? double.infinity,
|
||||||
|
)),
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(radius?? 32),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(text ?? "",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w300,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'pages/splash.dart';
|
||||||
import 'pages/login.dart';
|
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@ -13,7 +12,7 @@ class MyApp extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: const LoginPage(),
|
home: const SplashPage(),
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
//设置主题色
|
//设置主题色
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_qucikstart/common/button.dart';
|
||||||
|
|
||||||
import '../common/app_colors.dart';
|
import '../common/app_colors.dart';
|
||||||
import '../common/assets.dart';
|
import '../common/assets.dart';
|
||||||
@ -11,9 +12,133 @@ class LoginPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LoginPageState extends State<LoginPage> {
|
class _LoginPageState extends State<LoginPage> {
|
||||||
//登录表达
|
//检查账号输入是否有效
|
||||||
|
bool isUserNameValid = false;
|
||||||
|
//登录表单
|
||||||
Widget _buildForm() {
|
Widget _buildForm() {
|
||||||
return Container();
|
return Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 70, 20, 35),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(35),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
//Username or Email
|
||||||
|
const Text(
|
||||||
|
'Username or Email',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w300,
|
||||||
|
color: Color(0xFF838383),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
//Onchange时检测输入是否有效
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
isUserNameValid = value.isNotEmpty && value.length > 6;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: '@',
|
||||||
|
prefixIcon: Image.asset(
|
||||||
|
AssetsImages.iconUserPng,
|
||||||
|
width: 23,
|
||||||
|
height: 23,
|
||||||
|
),
|
||||||
|
suffixIcon: isUserNameValid == true
|
||||||
|
? const Icon(
|
||||||
|
Icons.done,
|
||||||
|
size: 24,
|
||||||
|
color: Colors.green,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 36),
|
||||||
|
|
||||||
|
//password
|
||||||
|
const Text(
|
||||||
|
'Password',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w300,
|
||||||
|
color: Color(0xFF838383),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
//隐藏输入
|
||||||
|
obscureText: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: '6 digits',
|
||||||
|
prefixIcon: Image.asset(
|
||||||
|
AssetsImages.iconLockPng,
|
||||||
|
width: 19,
|
||||||
|
height: 26,
|
||||||
|
),
|
||||||
|
suffixIcon: TextButton(
|
||||||
|
onPressed: (() {}),
|
||||||
|
child: const Text(
|
||||||
|
'Forget?',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Color(0xFF0274BC),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
//Sign In btn
|
||||||
|
ButtonWidget(
|
||||||
|
text: "Sign In",
|
||||||
|
height: 57,
|
||||||
|
onPressed:(){}
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
//Don't have an account? Sign up
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
//文字
|
||||||
|
const Text(
|
||||||
|
"Don't have an account?",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w300,
|
||||||
|
color: Color(0xFF171717),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
//文字按钮
|
||||||
|
TextButton(
|
||||||
|
onPressed: (() {}),
|
||||||
|
child: const Text(
|
||||||
|
"Sign up",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Color(0xFF0274bc),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
//end
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//主视图 拆成函数
|
//主视图 拆成函数
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../common/assets.dart';
|
import '../common/assets.dart';
|
||||||
|
import '../common/button.dart';
|
||||||
|
import '../pages/login.dart';
|
||||||
|
|
||||||
class WelcomePage extends StatelessWidget {
|
class WelcomePage extends StatelessWidget {
|
||||||
const WelcomePage({Key? key}) : super(key: key);
|
const WelcomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
//按钮组
|
//按钮组
|
||||||
Widget _bulidBtns() {
|
Widget _bulidBtns(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -26,28 +28,13 @@ class WelcomePage extends StatelessWidget {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|
||||||
//getstarted按钮
|
//getstarted按钮
|
||||||
//套一层Container,可以做很多事,比如设置尺寸
|
ButtonWidget(
|
||||||
Container(
|
text: "Get Started",
|
||||||
height: 42,
|
|
||||||
width: 139,
|
width: 139,
|
||||||
//需要裁切,不然没有圆角
|
height: 42,
|
||||||
clipBehavior: Clip.antiAlias,
|
radius: 32,
|
||||||
decoration: BoxDecoration(
|
onPressed: () => Navigator.push(context,
|
||||||
borderRadius: BorderRadius.circular(32),
|
MaterialPageRoute(builder: ((context) => const LoginPage()))),
|
||||||
),
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: (() {}),
|
|
||||||
style: ButtonStyle(
|
|
||||||
elevation: MaterialStateProperty.all(0),
|
|
||||||
//设置最小值为0,是的尺寸为children尺寸
|
|
||||||
minimumSize: MaterialStateProperty.all(Size.zero)),
|
|
||||||
child: const Text('Get Started',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.w300,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -69,7 +56,7 @@ class WelcomePage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildView() {
|
Widget _buildView(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -91,7 +78,7 @@ class WelcomePage extends StatelessWidget {
|
|||||||
height: 70,
|
height: 70,
|
||||||
),
|
),
|
||||||
//按钮组
|
//按钮组
|
||||||
_bulidBtns(),
|
_bulidBtns(context),
|
||||||
|
|
||||||
//end
|
//end
|
||||||
],
|
],
|
||||||
@ -101,7 +88,7 @@ class WelcomePage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Center(child: _buildView()),
|
body: Center(child: _buildView(context)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||