前言
如果你不是没有任何开发经验,那么你一定知道任何app里都有可能有重用性比较高的控件。所以对于那么重用性比较高的,或者需要你自定义的控件的,我们需要将它们给封装起来,以便下次或者其他app中继续使用。这也正式本节想要说的内容Flutter中如何封装Widget。
下面我从自己实现一个满意的封装,分别介绍你可能用到的三种封装方式
- 1、函数式封装
- 2、以继承 StatefulWidget 的方式封装
- 3、继承父类式封装(推荐)
下面我们以登录页的文本框的自定义来谈封装。

一、函数式封装
/// 蓝色背景按钮(常用于:登录按钮)
/// 方法1:以函数的方法实现
FlatButton blueButton(String text, bool enable, VoidCallback enableOnPressed) {
return FlatButton(
child: Text(text),
splashColor: Colors.transparent,
color: Color(0xff01adfe),
textColor: Colors.white,
highlightColor: Color(0xff1393d7),
disabledColor: Color(0xffd3d3d5),
disabledTextColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)
),
//onPressed: enable ? enableOnPressed : null,
onPressed: enable ? () {
enableOnPressed();
} : null,
);
}
乍看没什么问题,好像很简洁。但当你也用这种方式来实现文本框的时候,其代码如下:
/// 文本框(常用于:登录用户名、密码文本框)
/// 方法1:以函数的方法实现
TextField loginTextField(String placeholder, String prefixIconImageName, ValueChanged<String> onSubmitted) {
return TextField(
//autofocus: shouldAutofocusUserNameTextField,
style: TextStyle(color: Colors.black, fontSize: 17.0),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0.0),
//labelText: "用户名",
hintText: placeholder,
//prefixIcon: Icon(Icons.person),
prefixIcon: new Image.asset(
prefixIconImageName,
width: 14.0,
height: 15.0,
),
enabledBorder: loginTextFieldDecorationBorder(),
focusedBorder: loginTextFieldDecorationBorder(),
),
// keyboardType: TextInputType.text,
// controller: _usernameController,
// textInputAction: TextInputAction.next,
// focusNode: usernameFocusNode,
// onSubmitted: (text) {
// print("current userName:" + text);
// if (null == currentFocusNode) {
// currentFocusNode = FocusScope.of(context);
// }
// currentFocusNode.requestFocus(passwordFocusNode);
// }
onSubmitted: onSubmitted,
);
}
// 文本框border
InputBorder loginTextFieldDecorationBorder() {
return new OutlineInputBorder(
borderSide: new BorderSide(color: Color(0xffd2d2d2), width: 0.6),
borderRadius: new BorderRadius.circular(6.0)
);
}
可见这种函数的方式,没办法处理过多属性的自定义。因为它并不像我们iOS中的UIView,可以对得到的控件在后续再定制。所以,在Flutter中这种函数式的封装不适合,因为它无法满足使用。
附:以下是iOS中的操作:
- (CJTextField *)userNameTextField {
if (_userNameTextField == nil) {
UIImage *normalImage = [UIImage imageNamed:@"login_username_gray"];
UIImage *selectedImage = [UIImage imageNamed:@"login_username_blue"];
_userNameTextField = [CJDemoTextFieldFactory textFieldWithNormalImage:normalImage selectedImage:selectedImage];
_userNameTextField.placeholder = NSLocalizedString(@"用户名", nil);
_userNameTextField.returnKeyType = UIReturnKeyNext;
_userNameTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
_userNameTextField.delegate = self;
}
return _userNameTextField;
}
- (UIButton *)loginButton {
if (_loginButton == nil) {
_loginButton = [CJDemoButtonFactory blueButton];
[_loginButton setTitle:NSLocalizedString(@"登录", nil) forState:UIControlStateNormal];
_loginButton.enabled = NO;
[_loginButton addTarget:self action:@selector(loginButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _loginButton;
}
二、以继承 StatefulWidget 的方式封装
/// 文本框(常用于:登录用户名、密码文本框)
/// 方法2:以继承 StatefulWidget 的方式实现
class LoginTextField extends StatefulWidget {
final String placeholder;
final String prefixIconImageName;
final bool autofocus;
final TextEditingController controller;
final TextInputAction textInputAction;
final FocusNode focusNode;
final ValueChanged<String> onSubmitted;
final TextInputType keyboardType;
LoginTextField({
Key key,
this.placeholder,
this.prefixIconImageName,
this.autofocus,
this.keyboardType,
this.controller,
this.textInputAction,
this.focusNode,
this.onSubmitted
}) : super(key: key);
@override
_LoginTextFieldState createState() => _LoginTextFieldState();
}
class _LoginTextFieldState extends State<LoginTextField> {
@override
Widget build(BuildContext context) {
return Container(
child: TextField(
autofocus: widget.autofocus,
style: TextStyle(color: Colors.black, fontSize: 17.0),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0.0),
//labelText: "用户名",
hintText: widget.placeholder,
//prefixIcon: Icon(Icons.person),
prefixIcon: new Image.asset(
widget.prefixIconImageName,
width: 14.0,
height: 15.0,
),
enabledBorder: loginTextFieldDecorationBorder(),
focusedBorder: loginTextFieldDecorationBorder(),
),
keyboardType: widget.keyboardType,
controller: widget.controller,
textInputAction: widget.textInputAction,
focusNode: widget.focusNode,
onSubmitted: widget.onSubmitted
),
);
}
}
// 文本框border
InputBorder loginTextFieldDecorationBorder() {
return new OutlineInputBorder(
borderSide: new BorderSide(color: Color(0xffd2d2d2), width: 0.6),
borderRadius: new BorderRadius.circular(6.0)
);
}
使用的时候:
TextField userNameTextField() {
return TextField(
autofocus: shouldAutofocusUserNameTextField,
style: TextStyle(color: Colors.black, fontSize: 17.0),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0.0),
//labelText: "用户名",
hintText: "用户名",
//prefixIcon: Icon(Icons.person),
prefixIcon: new Image.asset(
userNameValid ? 'lib/Resources/login/login_username_blue.png' : 'lib/Resources/login/login_username_gray.png',
width: 14.0,
height: 15.0,
),
enabledBorder: loginTextFieldDecorationBorder(),
focusedBorder: loginTextFieldDecorationBorder(),
),
keyboardType: TextInputType.text,
controller: _usernameController,
textInputAction: TextInputAction.next,
focusNode: usernameFocusNode, //usernameFocusNode
onSubmitted: (text) {
print("current userName:" + text);
if (null == currentFocusNode) {
currentFocusNode = FocusScope.of(context);
}
currentFocusNode.requestFocus(passwordFocusNode);
}
);
}
虽然使用上看似没什么问题,但是整个TextField的继承代码难道你不觉得有更简洁的写法吗?
所以下面将讲解直接继承TextFiled的方法。
三、继承父类的方式
/// 文本框(常用于:登录用户名、密码文本框)
/// 方法3:以继承 TextField 的方式实现
class LoginTextField extends TextField {
LoginTextField({
Key key,
String text,
String placeholder,
/// prefix icon
bool prefixIconSelected,
String prefixIconNormalImageName,
String prefixIconSelectedImageName,
bool autofocus = false,
bool obscureText = false,
TextInputType keyboardType,
TextEditingController controller,
bool showClear = false,
TextInputAction textInputAction,
FocusNode focusNode,
ValueChanged<String> onSubmitted,
}) : super(
key: key,
autofocus: autofocus,
obscureText: obscureText,
style: TextStyle(color: Colors.black, fontSize: 17.0),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0.0),
//labelText: "用户名",
hintText: placeholder,
//prefixIcon: Icon(Icons.person),
prefixIcon: new Image.asset(
!prefixIconSelected ? prefixIconNormalImageName :prefixIconSelectedImageName,
width: 14.0,
height: 15.0,
),
suffixIcon: !showClear ? null : clearButtonWithOnPressed(controller.clear),
enabledBorder: loginTextFieldDecorationBorder(),
focusedBorder: loginTextFieldDecorationBorder(),
),
keyboardType: keyboardType,
controller: controller,
textInputAction: textInputAction,
focusNode: focusNode,
onSubmitted: onSubmitted
);
}
/// selected Image
class SelectedImage extends Image {
SelectedImage({
Key key,
bool selected,
String normalImageName,
String selectedImageName,
}) :super (
key: key,
image: AssetImage(!selected ? normalImageName :selectedImageName)
);
}
/// 文本框border
InputBorder loginTextFieldDecorationBorder() {
return new OutlineInputBorder(
borderSide: new BorderSide(color: Color(0xffd2d2d2), width: 0.6),
borderRadius: new BorderRadius.circular(6.0)
);
}
使用时候
// 用户名文本框
LoginTextField userNameTextField() {
return LoginTextField(
placeholder: "用户名",
prefixIconSelected: userNameValid,
prefixIconNormalImageName: 'assets/images/login/login_username_gray.png',
prefixIconSelectedImageName: 'assets/images/login/login_username_blue.png',
autofocus: shouldAutofocusUserNameTextField,
keyboardType: TextInputType.text,
controller: _usernameController,
textInputAction: TextInputAction.next,
focusNode: usernameFocusNode,
onSubmitted: (text) {
print("current userName:" + text);
if (null == currentFocusNode) {
currentFocusNode = FocusScope.of(context);
}
currentFocusNode.requestFocus(passwordFocusNode);
});
}
可见这种方式,不管在封装时候,还是在使用时候,写的代码都是最简洁的。
所以,综上在Flutter中对于一个Widget的封装,我们采用直接继承其父类的方式来处理,且其具体的写法如上。
网友评论