如何在 flutter 中像给定图像一样构建自定义 OTP 屏幕
How to build custom OTP screen like given image in flutter
我正在尝试构建如下图所示的 OTP 屏幕,但无法准确构建图像。
appLogo 应该在 TopCenter 中,Submit 按钮应该在 BottomCenter 中,OTP 元素应该在屏幕中央,OtpBox 应该像图片中那样有 1 个限制字符。
我尝试像这样构建但无法构建:
这里是我构建的屏幕:
我的代码:
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height/4.5,
width: MediaQuery.of(context).size.width/2.5,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/TriggerTrackerIcons/splash_logo@3x.png'),
)
),
),
SizedBox(height: 50,),
Container(
alignment: Alignment.centerLeft,
child: Text("Enter OTP:", style:TextStyle(fontWeight:FontWeight.bold, fontSize: 20, color: Colors.orange))
),
SizedBox(height: 8,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
otpBoxBuilder(),
SizedBox(width: 5,),
otpBoxBuilder(),
SizedBox(width: 5,),
otpBoxBuilder(),
SizedBox(width: 5,),
otpBoxBuilder(),
],
),
SizedBox(height: 50,),
InkWell(
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height/15,
color: Colors.orangeAccent,
child: Text("Submit", style:TextStyle(fontWeight:FontWeight.bold, fontSize: 20, color: Colors.white)),
),
onTap: (){
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>HomePage()));
},
),
],
),
),
),
),
);
OTP 盒生成器:
Widget otpBoxBuilder(){
return Container(
alignment: Alignment.center,
height: 70,
width: 70,
child: TextField(
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(
border: InputBorder.none
),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.blue)
),
);
}
你做对了,但需要一些调整和修改才能达到预期的效果 UI,
不要将所有内容都包裹在 SingleChildScrollView
中。它将使整个屏幕可滚动。
使用 Expanded
以及 Column
和 Container
来实现您的需要。请参考下面的代码片段。
Widget _body(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: _topLayout(context),
),
_bottomButton(context)
],
);
}
Widget _boxWithLable(BuildContext context) {
return Column(
children: <Widget>[
SizedBox(
height: 20,
),
Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 25),
alignment: Alignment.centerLeft,
child: Text("Boxes:",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.orange))),
_boxBuilder()
],
);
}
Widget _boxBuilder() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_box(),
_box(),
_box(),
_box(),
],
);
}
Widget _box() {
return Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 3),
alignment: Alignment.center,
height: 50,
width: 50,
child: TextField(
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(border: InputBorder.none, counterText: ''),
),
decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
);
}
Widget _headerImage() {
return Container(
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
child: Image.asset(
"assets/someimage.png",
fit: BoxFit.cover,
height: 160,
),
);
}
Widget _bottomButton(BuildContext context) {
return Container(
alignment: Alignment.topCenter,
child: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
print('clicked');
},
child: Text('Submit'),
),
SizedBox(
height: 20,
),
],
),
);
}
Widget _topLayout(BuildContext context) {
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[_headerImage(), _boxWithLable(context)],
),
);
}
现在在你的构建方法中调用 _body
Scaffold(
appBar: 'OTP',
body: _body(context),
)
请尝试告诉我它是否适合您。
我稍微修改了旅游箱的建造者代码!
并输入 OTP 容器边距 25,
注意:我没有那个大脑图像!
Widget otpBoxBuilder() {
return Container(
alignment: Alignment.center,
height: 70,
width: 70,
child: TextField(
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 23, color: Colors.blue),
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(border: InputBorder.none, counterText: ''),
textAlign: TextAlign.center,
),
decoration:
BoxDecoration(border: Border.all(color: Colors.blue, width: 3)),
);
}
@Ragu Swaminathan 我只是稍微修改了你的代码:
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(40.0),
child: _body(context),
),
);
}
Widget _body(BuildContext context) {
return Column(
children: <Widget>[
_headerImage(),
Expanded(
child: _boxWithLable(context),
),
_bottomButton(context)
],
);
}
Widget _boxWithLable(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
alignment: Alignment.centerLeft,
child: Text("Enter OTP:",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.orange
)
)
),
_boxBuilder()
],
);
}
Widget _boxBuilder() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_box(),
_box(),
_box(),
_box(),
],
);
}
Widget _box() {
return Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 3),
alignment: Alignment.center,
height: MediaQuery.of(context).size.height/14,
width: MediaQuery.of(context).size.width/8,
child: TextField(
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(
border: InputBorder.none,
counterText: '',
contentPadding: const EdgeInsets.all(20)
),
),
decoration: BoxDecoration(border: Border.all(color: Colors.blue,width: 2)),
);
}
Widget _headerImage() {
return Container(
padding: const EdgeInsets.only(top:25),
alignment: Alignment.topCenter,
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: Image.asset(
"assets/TriggerTrackerIcons/splash_logo@3x.png",
fit: BoxFit.cover,
height: 160,
),
);
}
Widget _bottomButton(BuildContext context) {
return Container(
alignment: Alignment.topCenter,
child: Column(
children: <Widget>[
InkWell(
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height/15,
color: Colors.orangeAccent,
child: Text("Submit", style:TextStyle(fontWeight:FontWeight.bold, fontSize: 20, color: Colors.white)),
),
onTap: (){
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>HomePage()));
},
),
SizedBox(
height: 50,
),
],
),
);
}
我正在尝试构建如下图所示的 OTP 屏幕,但无法准确构建图像。 appLogo 应该在 TopCenter 中,Submit 按钮应该在 BottomCenter 中,OTP 元素应该在屏幕中央,OtpBox 应该像图片中那样有 1 个限制字符。
我尝试像这样构建但无法构建:
这里是我构建的屏幕:
我的代码:
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height/4.5,
width: MediaQuery.of(context).size.width/2.5,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/TriggerTrackerIcons/splash_logo@3x.png'),
)
),
),
SizedBox(height: 50,),
Container(
alignment: Alignment.centerLeft,
child: Text("Enter OTP:", style:TextStyle(fontWeight:FontWeight.bold, fontSize: 20, color: Colors.orange))
),
SizedBox(height: 8,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
otpBoxBuilder(),
SizedBox(width: 5,),
otpBoxBuilder(),
SizedBox(width: 5,),
otpBoxBuilder(),
SizedBox(width: 5,),
otpBoxBuilder(),
],
),
SizedBox(height: 50,),
InkWell(
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height/15,
color: Colors.orangeAccent,
child: Text("Submit", style:TextStyle(fontWeight:FontWeight.bold, fontSize: 20, color: Colors.white)),
),
onTap: (){
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>HomePage()));
},
),
],
),
),
),
),
);
OTP 盒生成器:
Widget otpBoxBuilder(){
return Container(
alignment: Alignment.center,
height: 70,
width: 70,
child: TextField(
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(
border: InputBorder.none
),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.blue)
),
);
}
你做对了,但需要一些调整和修改才能达到预期的效果 UI,
不要将所有内容都包裹在 SingleChildScrollView
中。它将使整个屏幕可滚动。
使用 Expanded
以及 Column
和 Container
来实现您的需要。请参考下面的代码片段。
Widget _body(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: _topLayout(context),
),
_bottomButton(context)
],
);
}
Widget _boxWithLable(BuildContext context) {
return Column(
children: <Widget>[
SizedBox(
height: 20,
),
Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 25),
alignment: Alignment.centerLeft,
child: Text("Boxes:",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.orange))),
_boxBuilder()
],
);
}
Widget _boxBuilder() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_box(),
_box(),
_box(),
_box(),
],
);
}
Widget _box() {
return Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 3),
alignment: Alignment.center,
height: 50,
width: 50,
child: TextField(
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(border: InputBorder.none, counterText: ''),
),
decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
);
}
Widget _headerImage() {
return Container(
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
child: Image.asset(
"assets/someimage.png",
fit: BoxFit.cover,
height: 160,
),
);
}
Widget _bottomButton(BuildContext context) {
return Container(
alignment: Alignment.topCenter,
child: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
print('clicked');
},
child: Text('Submit'),
),
SizedBox(
height: 20,
),
],
),
);
}
Widget _topLayout(BuildContext context) {
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[_headerImage(), _boxWithLable(context)],
),
);
}
现在在你的构建方法中调用 _body
Scaffold(
appBar: 'OTP',
body: _body(context),
)
请尝试告诉我它是否适合您。
我稍微修改了旅游箱的建造者代码! 并输入 OTP 容器边距 25, 注意:我没有那个大脑图像!
Widget otpBoxBuilder() {
return Container(
alignment: Alignment.center,
height: 70,
width: 70,
child: TextField(
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 23, color: Colors.blue),
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(border: InputBorder.none, counterText: ''),
textAlign: TextAlign.center,
),
decoration:
BoxDecoration(border: Border.all(color: Colors.blue, width: 3)),
);
}
@Ragu Swaminathan 我只是稍微修改了你的代码:
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(40.0),
child: _body(context),
),
);
}
Widget _body(BuildContext context) {
return Column(
children: <Widget>[
_headerImage(),
Expanded(
child: _boxWithLable(context),
),
_bottomButton(context)
],
);
}
Widget _boxWithLable(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
alignment: Alignment.centerLeft,
child: Text("Enter OTP:",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.orange
)
)
),
_boxBuilder()
],
);
}
Widget _boxBuilder() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_box(),
_box(),
_box(),
_box(),
],
);
}
Widget _box() {
return Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 3),
alignment: Alignment.center,
height: MediaQuery.of(context).size.height/14,
width: MediaQuery.of(context).size.width/8,
child: TextField(
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(
border: InputBorder.none,
counterText: '',
contentPadding: const EdgeInsets.all(20)
),
),
decoration: BoxDecoration(border: Border.all(color: Colors.blue,width: 2)),
);
}
Widget _headerImage() {
return Container(
padding: const EdgeInsets.only(top:25),
alignment: Alignment.topCenter,
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: Image.asset(
"assets/TriggerTrackerIcons/splash_logo@3x.png",
fit: BoxFit.cover,
height: 160,
),
);
}
Widget _bottomButton(BuildContext context) {
return Container(
alignment: Alignment.topCenter,
child: Column(
children: <Widget>[
InkWell(
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height/15,
color: Colors.orangeAccent,
child: Text("Submit", style:TextStyle(fontWeight:FontWeight.bold, fontSize: 20, color: Colors.white)),
),
onTap: (){
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>HomePage()));
},
),
SizedBox(
height: 50,
),
],
),
);
}