当键盘在颤音中底部的底纸内部移动Textfield
我目前正在尝试创建某种Tiktok,例如Flutter中的评论部分。为此,我使用的是ModalBottomSheet和扩展的ListView,其中包含内部注释。但是,我未能归档我的Textfield在选择并出现键盘时会向上移动,这意味着我在选择后总是再看不到Textfield。我已经尝试使用FocusNode和Animation Controller,但是由于包含评论的Flex容器,它没有奏效...我知道这是很多代码,但请帮助我真的无法弄清楚。
底部表小部件:
void onCommentButtonPressed() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => Container(
height: MediaQuery.of(context).size.height * 0.75,
decoration: new BoxDecoration(
color: Colors.grey[900],
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(20.0),
topRight: const Radius.circular(20.0),
),
),
child: Column(
children: <Widget>[
Comments(
postId: targetId,
postOwnerId: ownerId,
),
],
),
),
);
}
其中的评论部分:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/Widgets/Images/profile_picture_small.dart';
import 'package:flutter_app/Widgets/header.dart';
import 'package:flutter_app/Widgets/progress.dart';
import 'package:flutter_app/constants.dart';
import 'package:provider/provider.dart';
import '../../Screens/Authenticate/authservice.dart';
import '../../Screens/Authenticate/database.dart';
import '../../models/user.dart';
import 'package:timeago/timeago.dart' as timeago;
class Comments extends StatefulWidget {
final String postId;
final String postOwnerId;
Comments({
required this.postId,
required this.postOwnerId,
});
@override
CommentsState createState() => CommentsState(
postId: postId,
postOwnerId: postOwnerId,
);
}
class CommentsState extends State<Comments> {
TextEditingController commentController = TextEditingController();
final String postId;
final String postOwnerId;
CommentsState({
required this.postId,
required this.postOwnerId,
});
@override
Widget build(BuildContext context) {
return Flexible(
child: Column(
children: [
Expanded(
child: buildComments(),
),
buildTextField(),
],
),
);
}
addComment(){
final currentUser = Provider.of<MyUser>(context, listen: false);
bool isNotPostOwner = postOwnerId != currentUser.id;
commentsRef.doc(postId).collection('comments').add({
'username': currentUser.username,
'comment': commentController.text,
'timestamp': DateTime.now(),
'avatarUrl': currentUser.photoUrl,
'userId': currentUser.id,
});
if(isNotPostOwner) {
activityFeedRef.doc(postOwnerId).collection('feedItems').add({
'type': 'comment',
'commentData': commentController.text,
'username': currentUser.username,
'userId': currentUser.id,
'userProfileImg': currentUser.photoUrl,
'postId': postId,
'timestamp': timestamp,
});
}
commentController.clear();
}
buildComments() {
return StreamBuilder<QuerySnapshot>(
stream: commentsRef.
doc(postId).
collection('comments').
orderBy('timestamp', descending: true).
snapshots(),
builder: (context, snapshot){
if (!snapshot.hasData){
return circularProgress();
}
else {
List<Comment> comments = [];
snapshot.data!.docs.forEach((doc){
comments.add(Comment.fromDocument(doc));
});
return ListView(children: comments,);
}
},
);
}
Widget buildTextField() {
return Container(
padding: EdgeInsets.symmetric(
vertical: kDefaultPadding / 2,
horizontal: kDefaultPadding / 2,
),
decoration: BoxDecoration(
color: Colors.transparent,
boxShadow: [
BoxShadow(
offset: Offset(0, 4),
blurRadius: 32,
color: Colors.blueGrey.withOpacity(0.1),
),
],
),
child: SafeArea(
child: Row(
children: [
ProfilePictureSmall(),
Padding(padding: EdgeInsets.symmetric(horizontal: 5)),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(
horizontal: kDefaultPadding * 0.75,
),
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(40),
),
child: Row(
children: [
SizedBox(width: kDefaultPadding / 4),
Expanded(
child: TextField(
controller: commentController,
decoration: InputDecoration(
hintText: "Write a comment...",
border: InputBorder.none,
),
),
),
InkWell(
onTap: () => addComment(),
child: Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(40.0),
),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(
Icons.arrow_upward_rounded,
color: Theme.of(context)
.textTheme
.bodyText1!
.color!
.withOpacity(0.64),
),
),
),
),
],
),
),
),
],
),
),
);
}
}
class Comment extends StatelessWidget {
final String username;
final String userId;
final String avatarUrl;
final String comment;
final Timestamp timestamp;
Comment({
required this.username,
required this.userId,
required this.avatarUrl,
required this.comment,
required this.timestamp,
});
factory Comment.fromDocument(DocumentSnapshot doc){
return Comment(
username: doc['username'],
userId: doc['userId'],
comment: doc['comment'],
timestamp: doc['timestamp'],
avatarUrl: doc['avatarUrl'],
);
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ListTile(
title: RichText(
text: TextSpan(text: '@...$username ',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
children: <TextSpan> [
TextSpan(text: '$comment',
style: Theme.of(context).textTheme.bodyText2),
]
),
),
leading: ProfilePictureSmall(),
subtitle: RichText(
text: TextSpan(
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.bold,
),
children: <TextSpan> [
TextSpan(text: '${timeago.format(timestamp.toDate(), locale: 'en_short')} ',
style: TextStyle(
color: Colors.grey[400],
fontWeight: FontWeight.w400,
),),
TextSpan(text: '193 Rockets ',
style: TextStyle(
color: Colors.grey[400]
),),
TextSpan(text: 'Reply',
style: TextStyle(
color: Colors.grey[400]
),
),
]
),
),
trailing: buildCommentFooter(context),
),
// Divider(color: Colors.white,),
],
);
}
buildCommentFooter(BuildContext context){
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: ()=> print('pressed'),
child: Icon(
Icons.monetization_on_rounded,
size: 20,
color: Colors.grey,
),
),
],
);
}
}
I am currently trying to create some kind of TikTok like comment section in flutter. For this I'm using a ModalBottomSheet and a Expanded Listview with the comments inside. However, I'm failing to archive that my TextField moves up when its selected and the Keyboard appears, meaning I always cant see the Textfield anymore after it's selection. I already tried using a focusnode and animation controller, however it didnt work out because of the Flex container which contains the comments... I know it's much code but please help I really cant figure it out.
The BottomSheet Widget:
void onCommentButtonPressed() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => Container(
height: MediaQuery.of(context).size.height * 0.75,
decoration: new BoxDecoration(
color: Colors.grey[900],
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(20.0),
topRight: const Radius.circular(20.0),
),
),
child: Column(
children: <Widget>[
Comments(
postId: targetId,
postOwnerId: ownerId,
),
],
),
),
);
}
and the comment section inside of it:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/Widgets/Images/profile_picture_small.dart';
import 'package:flutter_app/Widgets/header.dart';
import 'package:flutter_app/Widgets/progress.dart';
import 'package:flutter_app/constants.dart';
import 'package:provider/provider.dart';
import '../../Screens/Authenticate/authservice.dart';
import '../../Screens/Authenticate/database.dart';
import '../../models/user.dart';
import 'package:timeago/timeago.dart' as timeago;
class Comments extends StatefulWidget {
final String postId;
final String postOwnerId;
Comments({
required this.postId,
required this.postOwnerId,
});
@override
CommentsState createState() => CommentsState(
postId: postId,
postOwnerId: postOwnerId,
);
}
class CommentsState extends State<Comments> {
TextEditingController commentController = TextEditingController();
final String postId;
final String postOwnerId;
CommentsState({
required this.postId,
required this.postOwnerId,
});
@override
Widget build(BuildContext context) {
return Flexible(
child: Column(
children: [
Expanded(
child: buildComments(),
),
buildTextField(),
],
),
);
}
addComment(){
final currentUser = Provider.of<MyUser>(context, listen: false);
bool isNotPostOwner = postOwnerId != currentUser.id;
commentsRef.doc(postId).collection('comments').add({
'username': currentUser.username,
'comment': commentController.text,
'timestamp': DateTime.now(),
'avatarUrl': currentUser.photoUrl,
'userId': currentUser.id,
});
if(isNotPostOwner) {
activityFeedRef.doc(postOwnerId).collection('feedItems').add({
'type': 'comment',
'commentData': commentController.text,
'username': currentUser.username,
'userId': currentUser.id,
'userProfileImg': currentUser.photoUrl,
'postId': postId,
'timestamp': timestamp,
});
}
commentController.clear();
}
buildComments() {
return StreamBuilder<QuerySnapshot>(
stream: commentsRef.
doc(postId).
collection('comments').
orderBy('timestamp', descending: true).
snapshots(),
builder: (context, snapshot){
if (!snapshot.hasData){
return circularProgress();
}
else {
List<Comment> comments = [];
snapshot.data!.docs.forEach((doc){
comments.add(Comment.fromDocument(doc));
});
return ListView(children: comments,);
}
},
);
}
Widget buildTextField() {
return Container(
padding: EdgeInsets.symmetric(
vertical: kDefaultPadding / 2,
horizontal: kDefaultPadding / 2,
),
decoration: BoxDecoration(
color: Colors.transparent,
boxShadow: [
BoxShadow(
offset: Offset(0, 4),
blurRadius: 32,
color: Colors.blueGrey.withOpacity(0.1),
),
],
),
child: SafeArea(
child: Row(
children: [
ProfilePictureSmall(),
Padding(padding: EdgeInsets.symmetric(horizontal: 5)),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(
horizontal: kDefaultPadding * 0.75,
),
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(40),
),
child: Row(
children: [
SizedBox(width: kDefaultPadding / 4),
Expanded(
child: TextField(
controller: commentController,
decoration: InputDecoration(
hintText: "Write a comment...",
border: InputBorder.none,
),
),
),
InkWell(
onTap: () => addComment(),
child: Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(40.0),
),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(
Icons.arrow_upward_rounded,
color: Theme.of(context)
.textTheme
.bodyText1!
.color!
.withOpacity(0.64),
),
),
),
),
],
),
),
),
],
),
),
);
}
}
class Comment extends StatelessWidget {
final String username;
final String userId;
final String avatarUrl;
final String comment;
final Timestamp timestamp;
Comment({
required this.username,
required this.userId,
required this.avatarUrl,
required this.comment,
required this.timestamp,
});
factory Comment.fromDocument(DocumentSnapshot doc){
return Comment(
username: doc['username'],
userId: doc['userId'],
comment: doc['comment'],
timestamp: doc['timestamp'],
avatarUrl: doc['avatarUrl'],
);
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ListTile(
title: RichText(
text: TextSpan(text: '@...$username ',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
children: <TextSpan> [
TextSpan(text: '$comment',
style: Theme.of(context).textTheme.bodyText2),
]
),
),
leading: ProfilePictureSmall(),
subtitle: RichText(
text: TextSpan(
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.bold,
),
children: <TextSpan> [
TextSpan(text: '${timeago.format(timestamp.toDate(), locale: 'en_short')} ',
style: TextStyle(
color: Colors.grey[400],
fontWeight: FontWeight.w400,
),),
TextSpan(text: '193 Rockets ',
style: TextStyle(
color: Colors.grey[400]
),),
TextSpan(text: 'Reply',
style: TextStyle(
color: Colors.grey[400]
),
),
]
),
),
trailing: buildCommentFooter(context),
),
// Divider(color: Colors.white,),
],
);
}
buildCommentFooter(BuildContext context){
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: ()=> print('pressed'),
child: Icon(
Icons.monetization_on_rounded,
size: 20,
color: Colors.grey,
),
),
],
);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论