#android #flutter #dart
Вопрос:
В настоящее время я создаю 12 контейнеров для целей тестирования, размеры которых случайным образом определяются пропорционально размеру экрана устройства во время открытия приложения. Я показываю их на экране в качестве нижнего элемента структуры столбца в a SingleChildScrollView
, который я завернул в контейнер. Как вы можете видеть на изображении, когда я нажимаю на верхнюю строку поиска, клавиатура обычно открывается, но когда клавиатура открыта, размеры контейнеров изменяются. Когда клавиатура закрыта, их размер снова изменяется.
Что я хочу, чтобы это произошло, так это то, что контейнеры имеют размер один раз только при запуске приложения. Я хочу, чтобы их размер не менялся позже.
Вот функция, которую я написал для создания контейнеров:
_listAllCategories(int amount){
List<Widget> _even = [];
List<Widget> _single = [];
double width = MediaQuery.of(context).size.width*(5/11);
double height = MediaQuery.of(context).size.height/5;
for(int i = 0; i < amount; i ){
double rndHeight = height*randomDoubleInRange(min: 0.7, max: 1.3);
i.isEven ?
_even.add(Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: width,
height: rndHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.black12
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
'#etiket',
style: TextStyle(
fontFamily: 'JetBrainsMono-Regular',
),
),
),
Container(
width: width,
height: rndHeight - (height/5.5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.grey.shade900
),
),
],
),
),
)):
_single.add(Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: width,
height: rndHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.black12
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
'#etiket',
style: TextStyle(
fontFamily: 'JetBrainsMono-Regular',
),
),
),
Container(
width: MediaQuery.of(context).size.width*(5/11),
height: rndHeight - (height/5.5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.grey.shade900
),
),
],
),
),
));
}
return Container(
height: MediaQuery.of(context).size.height*(3.8/5),
child: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: _single
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: _even
),
],
),
),
);
}
Функция, которую я использую для генерации случайного double
значения в нужном мне диапазоне:
double randomDoubleInRange({double min = 0.0, double max = 1.0}) {
return Random().nextDouble() * (max - min 1) min;
}
Моя функция сборки находится здесь:
var _searchBarController;
@override
void initState() {
// TODO: implement initState
super.initState();
_searchBarController = TextEditingController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
drawer: myDrawer(),
backgroundColor: Color.fromARGB(255, 16,16,16),
body: Center(
child: Container(
child: Column(
children: [
SizedBox(height: 48,),
InkWell(
child: Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(left: 18.0),
child: Icon(Icons.search),
),
Container(
width: MediaQuery.of(context).size.width*(6.5/10),
height: 50,
child: Center(
child: TextFormField(
style: TextStyle(fontFamily: 'JetBrainsMono-Regular'),
controller: _searchBarController,
autofocus: false,
decoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding:
EdgeInsets.only(left: 15, bottom: 11, top: 11, right: 15),
hintText: "Etiketleri arayın.",
hintStyle: TextStyle(fontFamily: 'JetBrainsMono-Light')
),
),
),
),
PopupMenuButton(
child: Padding(
padding: const EdgeInsets.only(left: 18.0),
child: Icon(Icons.more_vert_outlined),
),
itemBuilder: (context){
return List.generate(5, (index){
return PopupMenuItem(child: Text('Item $index', style: TextStyle(fontFamily: 'JetBrainsMono-Regular'),));
});
},
),
],
),
width: MediaQuery.of(context).size.width*(9/10),
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Color.fromARGB(255,30,30,30),
),
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: Text(
'Tüm Etiketler',
style: TextStyle(
fontFamily: 'JetBrainsMono-Regular',
fontSize: 20
),
),
),
_listAllCategories(12)
],
),
),
),
);
}
Комментарии:
1. Можете ли вы также добавить код для своей функции сборки? Было бы неплохо привести минимально воспроизводимый пример.
2. Конечно, добавлена
build()
функция.3. Нормально ли, что функция сборки вызывается снова каждый раз, когда клавиатура открывается или закрывается? Потому что я никогда раньше не сталкивался ни с чем подобным.
4. @SeyitAhmet Обычно, но не обязательно.
5. @iDecode Как я могу предотвратить это?
Ответ №1:
Каждый раз, когда клавиатура появляется или опускается, функция build() вызывается в Flutter, потому что внешний вид экрана меняется. Вот почему у вас не может быть текстового поля в виджете без состояния (попробуйте, клавиатура попытается всплыть, но сразу же опустится).
Чтобы исправить это, вы можете присвоить значение, полученное из listAllCategories, переменной виджета во время initState (), а затем использовать эту переменную в функции сборки.
Поскольку вам нужна context
переменная в вашей функции, вы можете добавить обратный вызов после кадра, например:
Итак, ваш обновленный код:
В твоем классе:
var _searchBarController;
Widget categoryListing;
@override
void initState() {
// TODO: implement initState
super.initState();
_searchBarController = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((_) => initializeCategoryWidget(context));
}
initializeCategoryWidget(BuildContext context) {
setState(() {
categoryListing = _listAllCategories(12);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
drawer: myDrawer(),
backgroundColor: Color.fromARGB(255, 16,16,16),
body: Center(
child: Container(
child: Column(
children: [
SizedBox(height: 48,),
InkWell(
child: Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(left: 18.0),
child: Icon(Icons.search),
),
Container(
width: MediaQuery.of(context).size.width*(6.5/10),
height: 50,
child: Center(
child: TextFormField(
style: TextStyle(fontFamily: 'JetBrainsMono-Regular'),
controller: _searchBarController,
autofocus: false,
decoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding:
EdgeInsets.only(left: 15, bottom: 11, top: 11, right: 15),
hintText: "Etiketleri arayın.",
hintStyle: TextStyle(fontFamily: 'JetBrainsMono-Light')
),
),
),
),
PopupMenuButton(
child: Padding(
padding: const EdgeInsets.only(left: 18.0),
child: Icon(Icons.more_vert_outlined),
),
itemBuilder: (context){
return List.generate(5, (index){
return PopupMenuItem(child: Text('Item $index', style: TextStyle(fontFamily: 'JetBrainsMono-Regular'),));
});
},
),
],
),
width: MediaQuery.of(context).size.width*(9/10),
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Color.fromARGB(255,30,30,30),
),
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: Text(
'Tüm Etiketler',
style: TextStyle(
fontFamily: 'JetBrainsMono-Regular',
fontSize: 20
),
),
),
(categoryListing == null)?Container():categoryListing,
],
),
),
),
);
}
Комментарии:
1. Спасибо за ваш ответ, но он выдает ошибку. вызвал появление переменной
categoryWidget
какnull
. Я не понимаю, почему. Вот в чем ошибка:======== Exception caught by widgets library ======================================================= The following assertion was thrown building Homepage(dirty, dependencies: [MediaQuery], state: _HomepageState#e73d3): Column's children must not contain any null values, but a null value was found at index 3
2. Привет. Проверьте код сейчас. Я добавил условие, чтобы проверить, является ли виджет нулевым, прежде чем вводить его. Если он равен нулю, вы можете отобразить контейнер (), как я это сделал. Вы также можете обменять этот контейнер() на какой-нибудь другой виджет, например текст(«Загрузка…») или что-то в этом роде.
3. Спасибо @Zac! Я добавил a
CircularProgressIndicator()
к нулевой проверке. Тогда Это сработало!4. Рад помочь 🙂
Ответ №2:
Вы генерируете новые случайные размеры для каждого контейнера каждый раз, когда выполняется сборка, вместо этого вы можете создать другую функцию, которая будет запущена один раз в состоянии инициализации, эта функция может генерировать все размеры, а ваша функция _listAllCategories может просто использовать сгенерированные размеры.
Комментарии:
1. К сожалению, Флаттер этого не позволяет. Потому что я использую контекст, и я получаю ошибку, когда делаю это во время инициализации. Я пытался.
2. Ошибка в том , что:
The following assertion was thrown building Homepage(dirty, dependencies: [MediaQuery], state: _HomepageState#e73d3): Column's children must not contain any null values, but a null value was found at index 3
, но проблема была решена. Спасибо за вашу помощь.