在Flutter中,打開鍵盤后,我們可能希望底部的內容不被覆蓋。Flutter提供了一些方法來實現這一點。下面將從多個方面詳細闡述如何使用Flutter實現鍵盤頂起底部的效果。
一、使用SingleChildScrollView
使用SingleChildScrollView可以讓底部內容在鍵盤彈出時自動滾動到可見區域。我們只需要將底部內容包裹在SingleChildScrollView中,并在頁面初始化時獲得一個GlobalKey,然后在鍵盤彈出后通過該GlobalKey定位到bottom widget的位置,再通過動畫滾動到該位置。
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State {
final GlobalKey _bottomWidgetKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
// other widgets
Container(
key: _bottomWidgetKey,
child: // bottom widget
),
],
),
),
appBar: AppBar(
title: Text("Keyboard"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Show the keyboard
FocusScope.of(context).requestFocus(FocusNode());
},
child: Icon(Icons.add),
),
);
}
@override
void initState() {
super.initState();
final BottomWidgetRenderBox =
_bottomWidgetKey.currentContext.findRenderObject() as RenderBox;
SchedulerBinding.instance!.addPostFrameCallback((_) {
Future.delayed(const Duration(milliseconds: 300)).then((_) {
final RenderBox keyboard =
context.findRenderObject() as RenderBox;
final keyboardHeight = keyboard.size.height;
final heightDiff =
keyboardHeight - (MediaQuery.of(context).viewInsets.bottom);
final double offsetY =
heightDiff > 0 ? -(BottomWidgetRenderBox.size.height + heightDiff) : 0;
if (offsetY != 0) {
_controller.animateTo(
_controller.offset + offsetY,
duration: new Duration(milliseconds: 300),
curve: Curves.easeOut);
}
});
});
}
}
二、使用ListView
如果你希望底部內容可以滾動,我們可以使用ListView。ListView將自動在鍵盤彈出時滾動到底部,并且可以讓用戶滾動以查看所有內容。
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView(
children: [
// other widgets
// bottom widget
],
),
),
// input widget
],
),
appBar: AppBar(
title: Text("ListView"),
),
);
}
}
三、使用Stack和AnimatedPositioned
使用Stack和AnimatedPositioned可以在鍵盤彈出時自動調整底部內容的位置,使其不被鍵盤遮擋。我們可以將輸入框作為Stack的子元素,然后將底部內容作為Stack的第二個子元素。在鍵盤彈出時,我們可以使用AnimatedPositioned調整第二個子元素的位置。
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State {
GlobalKey _globalKey = GlobalKey();
double _bottom = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text("Stack and AnimatedPositioned"),
),
body: Stack(
children: [
Positioned(
top: 0,
bottom: 0,
left: 0,
right: 0,
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
child: Container(
color: Colors.white,
child: SingleChildScrollView(
child: Column(
key: _globalKey,
children: [
// other widgets
Container(
height: 800,
),
],
),
),
),
),
),
AnimatedPositioned(
duration: Duration(milliseconds: 300),
bottom: _bottom,
left: 0,
right: 0,
child: Container(
color: Colors.lightBlue,
height: 140,
child: Center(
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Text(
"Input",
style: TextStyle(
fontSize: 30,
color: Colors.white,
),
),
),
),
),
),
],
),
);
}
@override
void initState() {
super.initState();
final RenderBox renderBoxRed = _globalKey.currentContext.findRenderObject() as RenderBox;
WidgetsBinding.instance!.addPostFrameCallback((_) {
double height = renderBoxRed.size.height;
double screenHeight = MediaQuery.of(context).size.height;
double diff = screenHeight - height;
setState(() {
_bottom = diff;
});
});
}
}
四、結合SingleChildScrollView和Stack
我們也可以結合ScrollView和Stack來實現鍵盤頂起底部的效果。具體操作是將輸入框與底部內容放置在Stack中,并將Stack放置在SingleChildScrollView中。當鍵盤彈出時,可以與第一種方法類似地通過AnimationController將底部內容滑動到屏幕中央。
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State
with SingleTickerProviderStateMixin {
double _offset = 0.0;
bool _isKeyboardShowing = false;
late final AnimationController _controller = AnimationController(
duration: const Duration(milliseconds: 200), vsync: this);
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text("Stack and SingleChildScrollView"),
),
body: SingleChildScrollView(
child: Stack(
children: [
Column(
children: [
Container(
height: 800,
),
],
),
Positioned(
bottom: _offset,
left: 0,
right: 0,
child: Container(
color: Colors.lightBlue,
height: 140,
child: Center(
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Text(
"Input",
style: TextStyle(
fontSize: 30,
color: Colors.white,
),
),
),
),
),
),
],
),
),
);
}
@override
void initState() {
super.initState();
_controller.addListener(() {
setState(() {
_offset =
MediaQuery.of(context).viewInsets.bottom * _controller.value;
});
});
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {
_isKeyboardShowing = true;
});
} else if (status == AnimationStatus.dismissed) {
setState(() {
_isKeyboardShowing = false;
});
}
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text("Stack and SingleChildScrollView"),
),
body: SingleChildScrollView(
child: Stack(
children: [
Column(
children: [
Container(
height: 800,
),
GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
}
child: // input widget
),
],
),
Positioned(
bottom: _offset,
left: 0,
right: 0,
child: Container(
color: Colors.lightBlue,
height: 140,
child: Center(
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Text(
"Input",
style: TextStyle(
fontSize: 30,
color: Colors.white,
),
),
),
),
),
)
],
),
),
);
}
void _handleFocusChange() {
if (FocusScope.of(context).hasFocus != _isKeyboardShowing) {
_isKeyboardShowing = FocusScope.of(context).hasFocus;
_controller.animateTo(
_isKeyboardShowing ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
curve: Curves.linear);
}
}
}
五、小結
本文介紹了Flutter中實現鍵盤頂起底部的幾種方法,包括使用SingleChildScrollView、ListView、Stack以及結合方法。在使用這些方法時,我們需要考慮底部內容的特殊結構,確保鍵盤彈出時,底部內容不會被遮擋。希望本文能對您在Flutter開發中實現鍵盤頂起底部的需求提供幫助。