设计给的效果如下:
拿到设计后,先把整体拆分成几个部分:
然后就可以开始进行编码了。
下面的代码导入了一堆东西,大概说一下都是些什么吧。../common/first_navigation.dart
是《Flutter布局锦囊—简单的应用栏》,不过针对这个需求做了一些小改动。../common/doodle_button.dart
是《Flutter布局锦囊—涂鸦风格按钮》。../common/round_form_field.dart
是《Flutter布局锦囊—圆框的表单字段》。widgets/login_form_code.dart
是《Flutter布局锦囊—验证码倒计时》。
import 'package:flutter/material.dart'; import '../common/first_navigation.dart'; import '../common/doodle_button.dart'; import '../common/round_form_field.dart'; import 'widgets/login_form_code.dart'; /// 自定义的绑定页面组件。 class Binding extends StatefulWidget { @override _BindingState createState() => _BindingState(); }
下面代码的主体是《Flutter布局锦囊—简单的应用栏》中自定义的一级导航(firstNavigation
)组件,不过这里你要把行动(actions
)和主导(leading
)属性设置成可以配置的,如果没有则为空值(null
)。
/// 与自定义的绑定页面组件关联的状态子类。 class _BindingState extends State<Binding> { /// 手机号表单字段的控制器。 final _phoneController = TextEditingController(); /// 验证码表单字段的控制器。 final _codeController = TextEditingController(); /// 发送验证码按钮是否可用。 bool _codeAvailable = false; /// 手机号表单字段是否符合格式。 bool _phoneActivation = false; /// 验证码表单字段是否符合格式。 bool _codeActivation = false; @override Widget build(BuildContext context) { return Scaffold( appBar: firstNavigation( '绑定手机', // 墨水瓶(`InkWell`)组件,响应触摸的矩形区域。 leading: InkWell( child: Text( ' 取消 ', style: TextStyle( color: Color(0xFF777777), fontSize: 16.0, ), ), onTap: (){ Navigator.of(context).pop(); }, ), actions: InkWell( child: Text( ' 跳过 ', style: TextStyle( color: Color(0xFFFF6B47), fontSize: 16.0, ), ), onTap: (){ Navigator.of(context).pushNamedAndRemoveUntil('/', (Route<dynamic> route) => false); }, ), ), body: ListView( padding: EdgeInsets.symmetric(horizontal: 20.0), children: <Widget>[ // TODO: 第3步:实现“手机号输入框” // TODO: 第4步:实现“验证码输入框” // TODO: 第5步:实现“提交按钮” ], ), ); } }
下面代码的主体是《Flutter布局锦囊—圆框的表单字段》中自定义的圆框表单字段(RoundFormField
)组件。这个就可以直接使用了,不用像上面那一步修改代码。
// TODO: 第3步:实现“手机号输入框” Container( padding: EdgeInsets.symmetric(vertical: 20.0), child: Text( '绑定手机后可以直接使用手机号码登录', style: TextStyle( color: Color(0xFF777777), fontSize: 14.0, ), ), ), RoundFormField( hintText: '请输入手机号', textEditingController: _phoneController, checkCallback: (value){ if(value.trim().length == 11) { _codeAvailable = true; _phoneActivation = true; } else { _codeAvailable = false; _phoneActivation = false; } setState(() {}); }, ), SizedBox(height: 20),
下面代码的主体是《Flutter布局锦囊—圆框的表单字段》中自定义的圆框表单字段(RoundFormField
)组件,再佐以《Flutter布局锦囊—验证码倒计时》中自定义的倒计时(LoginFormCode
)组件作为调料。它们共同完成了验证码输入框。
// TODO: 第4步:实现“验证码输入框” Stack( children: <Widget>[ RoundFormField( hintText: '请输入验证码', textEditingController: _codeController, checkCallback: (value){ if(value.trim().length == 6) { _codeActivation = true; } else { _codeActivation = false; } setState(() {}); }, ), // 对齐(`Align`)组件,用于将其子项与其自身对齐,并根据子级的大小自行调整大小。 Align( // 高度因子(`heightFactor`)属性,如果为非空值,则将其高度设置为子组件高度乘以此系数。 heightFactor: 2.0, // 对准(`alignment`)属性,如何调整子组件。 alignment: Alignment.centerRight, child: LoginFormCode( countdown: 60, available: _codeAvailable, onTapCallback: () { // 根据需求写提交的过程以及后续操作~ }, ), ), ], ),
下面代码的主体是《Flutter布局锦囊—涂鸦风格按钮》中自定义的涂鸦按钮(DoodleButton
)组件,注意要通过setState(() {});
来改变按钮的可点击状态。
// TODO: 第5步:实现“提交按钮” SizedBox(height: 40), DoodleButton( promptText: '绑定', activation: _phoneActivation && _codeActivation, onTapCallback: (){ setState(() { _codeActivation = false; }); // 根据需求写提交的过程以及后续操作~ setState(() { _codeActivation = true; }); }, ),