Reputation: 363
I am currently creating 12 containers for testing purposes, whose dimensions are randomly determined in proportion to the screen size of the device during application opening. I show them on the screen as the bottom element of the column structure in a SingleChildScrollView
that I wrapped in a container. As you can see from the image, when I click on the top search bar, the keyboard normally opens, but when the keyboard is opened, the containers are resized. When the keyboard is closed, these are resized again.
What I want it to happen is that the containers are sized one time only at application startup. I want them not to be resized later.
Here is the function I wrote to create containers:
_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
),
],
),
),
);
}
The function I use to generate a random double
value in the range I want:
double randomDoubleInRange({double min = 0.0, double max = 1.0}) {
return Random().nextDouble() * (max - min + 1) + min;
}
My build function is here:
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)
],
),
),
),
);
}
Upvotes: 2
Views: 221
Reputation: 1103
Every time the keyboard shows up or goes back down, the build() function in called in Flutter, because the look of the screen is changing. That's why you can't have a TextField in a Stateless Widget (try it out, the keyboard will try popping up, but will immediately go back down).
To fix this, you can assign the value coming from listAllCategories to a widget variable during initState(), and then use that variable in your build function.
Since you need the context
variable in your function, you can add a postframe callback like so:
So your updated code is:
In your class:
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,
],
),
),
),
);
}
Upvotes: 1
Reputation: 908
You are generating new random sizes for each container everytime build runs instead you can create another function that will be run once in init state, That function can generate all of the sizes and your _listAllCategories function can simply use the sizes that was generated.
Upvotes: 0