Как переместить нижний лист вместе с клавиатурой с текстовым полем (автофокусировка - правда)?
Я пытаюсь создать нижнюю таблицу, в которой есть текстовое поле, а для автофокуса установлено значение true, чтобы клавиатура всплывала. Но нижний лист перекрывается клавиатурой. Есть ли способ переместить нижнюю панель над клавиатурой?
Padding(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(children: <Widget>[
TextField(
autofocus: true,
decoration: InputDecoration(hintText: 'Title'),
),
TextField(
decoration: InputDecoration(hintText: 'Details!'),
keyboardType: TextInputType.multiline,
maxLines: 4,
),
TextField(
decoration: InputDecoration(hintText: 'Additional details!'),
keyboardType: TextInputType.multiline,
maxLines: 4,
),]);
28 ответов
В флаттере 1.7.X и позже добавлены дополнительные функции BottomSheetDialog
поэтому в дополнение к anmol.majhail вы можете добавить ответ isScrollControlled = true
в showModalBottomSheet
это позволит нижнему листу занять всю необходимую высоту, что дает больше страховки, что TextField
не покрыта клавиатурой.
Нравится:
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0))),
backgroundColor: Colors.black,
context: context,
isScrollControlled: true,
builder: (context) => Padding(
padding: const EdgeInsets.symmetric(horizontal:18 ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text('Enter your address',
style: TextStyles.textBody2),
),
SizedBox(
height: 8.0,
),
Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: TextField(
decoration: InputDecoration(
hintText: 'adddrss'
),
autofocus: true,
controller: _newMediaLinkAddressController,
),
),
SizedBox(height: 10),
],
),
));
Обратите внимание, что:
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0))),
Это не обязательно. Просто я создаю закругленный нижний лист.
Обновить
Если твой BottomSheetModel
является Column
убедитесь, что вы добавляете mainAxisSize: MainAxisSize.min,
в противном случае лист будет покрывать весь экран.
Просто добавь:
isScrollControlled: true
показать ModalBottomSheetpadding: MediaQuery.of(context).viewInsets
к виджету в конструкторе- столбец / обернуть обе работы
showModalBottomSheet<void>(
isScrollControlled: true,
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0)),
),
builder: (BuildContext context) {
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: Container(
child: Wrap(
children: <Widget>[
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'),
),
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'),
),
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'),
),
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'),
)
],
)));
},
);
Обновите 25 февраля 2020 г. лучшее решение
showModalBottomSheet(
isScrollControlled: true,
builder: (BuildContext context) {
return SingleChildScrollView(
child: Container(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Padding(
padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0), // content padding
child: Form(...)) // From with TextField inside
});
Для того, чтобы сосредоточиться на клавиатуре в BottomSheet
- Заворачивать TextField
в Padding Widget, как показано ниже, например, Code:
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: TextField(
autofocus: true,
),
),
);
});
В последней версии flutter вы можете перемещать свой bottomSheet с помощью isScrollControlled
свойство / именованный параметр. Предположим, у меня есть функция (_showModal), которая будет вызываться при нажатии кнопки. И я определяю функциональность нижнего листа для этой функции.
void _showModal() {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Column(
children: <Widget>[
TextField(// your other code),
SizedBox(height: 5.0),
TextField(// your other code),
SizedBox(height: 5.0),
TextField(// your other code),
]
);
},
);
}
Здесь появится ModalBottomSheet, но с полноэкранной высотой. И такая высота тебе не нужна. Итак, вам нужноmainAxisSize
к min
.
Column(
mainAxisSize: MainAxisSize.min,
// your other code
)
Полноэкранная проблема с высотой решена, но ModalBottomSheet не перемещается вверх, когда появляется клавиатура. Хорошо, чтобы решить эту проблему, вам нужно установитьviewInsets
нижний отступ к вашему ModalBottomSheet. Итак, чтобы установить отступ, нам нужно обернуть нашу колонку контейнером или заполнением, а затем установить отступ. Окончательный код будет выглядеть так
void _showModal() {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
// You can wrap this Column with Padding of 8.0 for better design
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(// your other code),
SizedBox(height: 5.0),
TextField(// your other code),
SizedBox(height: 5.0),
TextField(// your other code),
]
),
);
},
);
}
Надеюсь, ваша проблема исправлена. Благодарность
Пакет: https://pub.dev/packages/modal_bottom_sheet
Оберните свой виджет в Padding и установите его следующим образом ==>
padding: MediaQuery.of(context).viewInsets // viewInsets will decorate your screen
Вы можете использовать showMaterialModalBottomSheet или showModalBottomSheet или showCupertinoModalBottomSheet.
showModalBottomSheet(
context: context,
barrierColor: popupBackground,
isScrollControlled: true, // only work on showModalBottomSheet function
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(borderRadiusMedium),
topRight: Radius.circular(borderRadiusMedium))),
builder: (context) => Padding(
padding: MediaQuery.of(context).viewInsets,
child: Container(
height: 400, //height or you can use Get.width-100 to set height
child: <Your Widget here>
),)),)
Попробуй это
Мое решение
- Использовать
isScrollControlled: true,
- Добавить отступ
padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom)
- Оберните свой макет в
SingleChildScrollView
ОБРАЗЕЦ КОДА
Future<void> future = showModalBottomSheet(
context: context,
isDismissible: true,
isScrollControlled: true,
backgroundColor: Colors.white.withOpacity(0.2),
builder: (context) => SingleChildScrollView(
child: GestureDetector(
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: <Widget>[
// add your widget here
],
),
),
)),
));
Для людей, которые не могут решить свою проблему, пытаясь найти все ответы. Эти ответы верны, но не так однозначны.
Когда используешь
MediaQuery.of(context).viewInsets.bottom)
builder :(**c**)=>MediaQuery.of(**c**)
Я исправил это, увеличив высоту дочернего виджета при открытии клавиатуры. изначально значение MediaQuery.of(context).viewInsets.bottom будет равно 0, оно изменится, когда клавиатура получит фокус.
showModalBottomSheet<void>(
enableDrag: true,
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Card(
color: Colors.white,
child: Container(
height: MediaQuery.of(context).size.height / 2 +
MediaQuery.of(context).viewInsets.bottom,
child: Column(
children: <Widget>[
TextField(),
TextField(),
],
),
),
);
},
);
ОБНОВЛЕНО 2021!
Это верный ответ , но теперь вам не нужно добавлять отступы снизу! Найдите и удалите эту строку:
padding: MediaQuery.of(context).viewInsets
Добавьте это после последнего виджета вашего нижнего листа
Padding(padding: EdgeInsets.only(bottom:MediaQuery.of(context).viewInsets.bottom))
Тогда вам велено использовать это,
showModalBottomSheet(
isScrollControlled: true,
context: context,
shape: RoundedRectangleBorder(
// <-- for border radius
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
),
),
builder: (BuildContext context) {
return SingleChildScrollView(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: drunkenWidget()...
//BTW, Never ever Drink
Попробуй это.
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
duration: const Duration(milliseconds: 100),
curve: Curves.decelerate,
child: Container(
child: Wrap(
children: [
TextField(
decoration: InputDecoration(labelText: "1"),
),
TextField(
decoration: InputDecoration(labelText: "2"),
),
TextField(
decoration: InputDecoration(labelText: "3"),
),
],
)));
},
)
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
context: context,
isScrollControlled: true,
builder: (builder) {
return Container(
height: MediaQuery.of(context).size.height - 40,
padding: MediaQuery.of(context).viewInsets,
child: <Your Widget Here>,
);
},
);
В моем случае это сработало только при добавлении этой строки вScaffold
внутри страницы, кто звонит:
Scaffold(
resizeToAvoidBottomInset: false,
..
),
И обязательно нужно добавитьpadding
внутриshowModalBottomSheet
хbuilder
:
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Container(), // Your layout goes here..
);
}
);
Вместо того, чтобы использовать
builder: (BuildContext context) { }
в конструкторе используйте
builder: (context) { }
С этим решением мой модальный нижний лист прикрепляется к строке состояния (действует как
Scaffold
с участием
resizeToAvoidBottomInset: false
) и позволяет просматривать все поля формы и прокручивать форму, если она все еще необходима для просмотра нижних текстовых полей.
Для получения более подробной информации вот ссылка, по которой я нашел решение - https://github.com/flutter/flutter/issues/18564#issuecomment-602604778
Если у вас полный экран или фиксированный размер showModalBottomSheet
не использовать padding
это не решит вашу проблему. Использоватьmargin
вместо того padding
нравится:
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
marign: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: TextField()
);
});
Загрузите это на github
Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: TextField()
)
Если вы все еще не нашли свою проблему. Поэтому я думаю, что вы пропустили свой BuilderContext. Иногда, когда вы реализуете modalBottomSheet, он дает вам только параметр контекста. Поэтому добавьте контекст с помощью BuildContext.
builder: (BuildContext context) { //-Here is your issue add BuilderContext class name as it as
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: new Container(
После объединения разных решений я получил следующее:
если вы не хотите, чтобы он был полноэкранным и не хотите использовать обходной путь Padding, используйте
showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
enableDrag: true,
isDismissible: true,
useRootNavigator: true,
builder: (BuildContext ctx) {
return Scaffold( // use CupertinoPageScaffold for iOS
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: true, // important
body: SingleChildScrollView(
child: Form(
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormField(),
TextFormField(),
],
),
),
),
),
);
},
);
на Flutter (мастер канала, v1.15.3-pre.37, в Mac OS X 10.15.2 19C57, локаль en-US)
Добавьте это в модальный нижний лист вашего маршрутизатора
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return Messagescr();
}
);
И удалите эту строку также в вашем Scaffold:
resizeToAvoidBottomInset : false,
showModalBottomSheet(
isScrollControlled: true,
builder: (BuildContext context) {
return SingleChildScrollView(
child: Container(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Padding(
padding: const EdgeInsets.all(15.0), // content padding
child: Container())});
Примечание: эта строка творит всю магию
Обернуть Form
с Scaffold
виджет, а затем обернуть TextFormField
с SingleChildScrollView
:
return Container(
height: screenHeight * .66,
child: Scaffold(
child: Form(
key: _form,
child: SingleChildScrollView(
child:TextFormField()
)
)
)
)
Прежде чем попробовать любое из вышеперечисленных решений, не забудьте добавить
все решения хороши. но для модальных окон с фиксированным заголовком и длинным телом с текстовым полем внизу я рекомендовал это решение. эта высота больше, чем screen.height/2
showMaterialModalBottomSheet<void>(
// isScrollControlled: true,
context: context,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
// side: BorderSide(color: Colors.grey),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
),
builder: (BuildContext context) {
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: Container(
height: height - MediaQuery.of(context).viewInsets.bottom,
decoration: BoxDecoration(
color: ColorConstants.background,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
),
padding:EdgeInsets.all(0),
child: Wrap(
children: [
// fixed header
Container(
height: 56,
alignment: Alignment.center,
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
),
),
SizedBox(
height: height -
56 -
MediaQuery.of(context).viewInsets.bottom,
child: (scrollable body with textfield))
],
)));
},
);
Простое решение, вы можете настроить это:
Container(
margin: EdgeInsets.only(left: 15),
child: InkWell(
onTap: () {
showModalBottomSheet(
isScrollControlled : true,
context: context,
backgroundColor: Colors.transparent,
builder: (context) {
return Container(
padding: EdgeInsets.only(top: 15, left: 15, right: 15, bottom: 10),
width: double.infinity,
decoration: BoxDecoration(
color: AppTheme.leadItemColor1,
borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
),
child: Column(
children: [
_assignTo(widget.viewModel, context),
SizedBox(height: 12,),
txtComment(widget.viewModel),
SizedBox(height: 12,),
CRMButton(
title: 'Select',
onTap: () async {
Navigator.pop(context);
await widget.viewModel.updateStatus(7, why: "${ConstantData.lostOptions[_selectedNumber]}");
},
)
],
),
);
},
);
},
child: CustomTabBarItem1(
image: widget.viewModel.leadDetail.success.lStatus == 7 ? 'assets/appimages/LeadDetail/icons-03-01.png' : 'assets/appimages/LeadDetail/icons-04-01.png',
bottomTitle: 'Lost',
topTitle: widget.viewModel.leadDetail.success.lStatus > 7 ? 'assets/appimages/LeadDetail/Ellipse 61@2x.png' : widget.viewModel.leadDetail.success.lStatus == 7 ? 'assets/appimages/LeadDetail/Group 486-1.png' : 'assets/appimages/LeadDetail/Ellipse-61@3x.png',
height : widget.viewModel.leadDetail.success.lStatus == 7 ? "0" : "1",
)),
),
В ответе Эбеда указано правильное решение вашего конкретного вопроса. Однако есть много других сложных вопросов, которые необходимо решить при создании нижнего листа. Вот моя полная работающая функция создания нижнего листа, которая, надеюсь, кому-нибудь поможет.
import 'package:flutter/material.dart';
Future Function() openBottomSheet(BuildContext context, Widget child,
{String? title}) {
return () => showModalBottomSheet(
// Don't let bottom sheet extend past the top of the safe area
useSafeArea: true,
context: context,
// Round the top of the bottom sheet
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
// Add scrollbar when necessary
isScrollControlled: true,
builder: (context) => ScrollableWidget(
child: Container(
// Move bottom sheet above on-screen keyboard, if keyboard is open
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Stack(
children: [
// Add back button at top left of bottom sheet (since it's
// not obvious that sheet can be swiped down to close it)
const BackButton(),
Padding(
// Pad the main widget (larger padding on the top to leave
// space for the back button)
padding: const EdgeInsets.fromLTRB(20.0, 35.0, 20.0, 25.0),
child: Column(
children: [
// Make content full-width, so that main widget is
// centered even if it doesn't expand
Row(
mainAxisSize: MainAxisSize.max,
children: const [SizedBox(height: 0)],
),
// Add title text to top of bottom sheet, if provided
title == null
? Container()
: Column(
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
],
),
// Add the main widget
child,
],
),
),
],
),
),
),
);
}
/// A scrollable widget with a scrollbar that is shown when necessary
class ScrollableWidget extends StatefulWidget {
const ScrollableWidget({super.key, required this.child});
final Widget child;
@override
State<ScrollableWidget> createState() => _ScrollableWidgetState();
}
class _ScrollableWidgetState extends State<ScrollableWidget> {
final controller = ScrollController();
@override
Widget build(BuildContext context) {
return Scrollbar(
thumbVisibility: true,
thickness: 15,
radius: const Radius.circular(8),
controller: controller,
child: SingleChildScrollView(
// Same controller must be used on Scrollbar and SingleChildScrollView
controller: controller,
child: widget.child,
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
Просто добавь
if (_isEditing) SizedBox(height: MediaQuery.of(context).viewInsets.bottom),
под клавиатурой и скрыть все остальное содержимое под текстовым полем с помощью
if (!_isEditing) Widget(...),