本文按照由里到外,从简单开始一步步向外的顺序。完整代码在最后一段,处理Textfield与showModalBottomSheet冲突导致键盘遮挡的bug 的代码在倒数第二段
准备
要实现的动画效果
需要用到如下的 widget / attribute / method
bottomNavigationBar
showModalBottomSheet
AnimatedPadding
TextField
制作文本框Textfield
Row textField() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Expanded(
child: new TextField(
decoration: InputDecoration(
hintText: 'Say something here...', //提示文字
border: null,
focusedBorder: UnderlineInputBorder( //输入框被选中时的边框样式
borderSide: BorderSide(color: Colors.blue[300]),
),
),
keyboardType: TextInputType.text, //键盘的类型
maxLength: 250, //最多字数
maxLines: 10, //最高行数
),
),
IconButton(
icon: Icon(Icons.send),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
}
首先要使输入框和发布按钮在一行,用Row组件将Textfield & IconButton包裹起来
IconButton 的onPressed方法中 Navigator.of(context).pop(); 使得在点击发送按钮后,可以自动关闭页面
适当调整样式
制作showModalBottomSheet
showModalBottomSheet是从屏幕下方弹出的一个不占用屏幕全部空间的小页面,在显示时屏幕背景会变暗
showModalBottomSheet(
context: context,
builder: (BuildContext context){
return Container(
child: textField(),
padding: EdgeInsets.all(7), //在textField()的所有方向加7单位的边距(空白)
);
}
);
此时用了一个Container包裹Textfield目的是为了加边距(当然也有其他的选择)
制作bottomButton
底部评论按钮,要置底,不能随着页面的滑动串位。这时可能考虑到用绝对定位,不过有更好的选择:bottomNavigationBar
在Scaffold中提供了bottomNavigationBar属性,该属性提供了:
BottomNavigationBar用于底部导航栏,BottomAppBar用于底部应用栏
BottomAppBar可以自定义,所以使用它放置底部评论按钮
bottomNavigationBar: BottomAppBar(
child: bottomNewCommentButton(),
),
代码很简单,一目了然
下面构造评论按钮
Container bottomNewCommentButton(){
return Container(
child: RaisedButton(
child: Text("Publish", style: TextStyle(fontSize: 20.0, color: Colors.white)),
color: Colors.blue[300],
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context){
return Container(
child: textField(),
padding: EdgeInsets.all(7),
);
}
);
},
),
height: 50,
);
}
在onPressed中放入刚才写好的showModalBottomSheet这样点击的时候bottomsheet就会显示出来
解决bug
写到这里应该就会发现Textfield在bottomsheet中的问题:键盘会遮挡bottomsheet
经过测试后发现,添加Textfield焦点是法解决问题的
解决方法:
showModalBottomSheet(
context: context,
builder: (BuildContext context){
return new AnimatedPadding(
padding: MediaQuery.of(context).viewInsets, //边距(必要)
duration: const Duration(milliseconds: 100), //时常 (必要)
child: Container(
child: textField(),
padding: EdgeInsets.all(7),
),
);
}
);
在showModalBottomSheet中,原有的Textfield组件外嵌套AnimatedPadding,AnimatedPadding其实是缩进变化时的动画,padding和duration是它的必要属性,缺失会报错 。
AnimatedPadding api: api.flutter.dev/flutter/wid…
完整代码
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'showModalBottomSheet',
home: BasicPage(),
);
}
}
class BasicPage extends StatefulWidget {
@override
_BasicPageState createState() => _BasicPageState();
}
class _BasicPageState extends State<BasicPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("showModalBottomSheet"),
),
body: Center(
),
bottomNavigationBar: BottomAppBar(
child: bottomNewCommentButton(),
),
);
}
Container bottomNewCommentButton(){
return Container(
child: RaisedButton(
child: Text("Publish", style: TextStyle(fontSize: 20.0, color: Colors.white)),
color: Colors.blue[300],
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context){
return new AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
duration: const Duration(milliseconds: 100),
child: Container(
child: textField(),
padding: EdgeInsets.all(7),
),
);
}
);
},
),
height: 50,
);
}
Row textField() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Expanded(
child: new TextField(
decoration: InputDecoration(
hintText: 'Say something here...',
border: null,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue[300]),
),
),
keyboardType: TextInputType.text,
maxLength: 250,
maxLines: 10,
),
),
IconButton(
icon: Icon(Icons.send),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
}
}