Related
The SingleChildScrollView is surrounded by the Flexible widget
because there's an overflow error. However, placing the Flexible
then makes the text too small!?
Please, how can I do this in a better way?
#override
Widget build(BuildContext context) {
return Scaffold(
// primary: false,
appBar: AppBar(
title: I10n.t('Playhouse'),
centerTitle: true,
automaticallyImplyLeading: false,
elevation: 0,
excludeHeaderSemantics: true,
),
endDrawer: const ScrapBookDrawer(),
body: SafeArea(
child: Column(
children: [
/// Submodule Name | Short Description
Flexible(
flex: 3,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
widget.subTask['subName'],
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
const Flexible(
child: Padding(
padding: EdgeInsets.only(top: 4),
child: Text(
'Submodule description',
),
),
),
],
),
),
Flexible(
flex: 5,
fit: FlexFit.tight,
child: Stack(
alignment: AlignmentDirectional.topCenter,
children: <Widget>[
/// Large Picture
Crop(
interactive: false,
backgroundColor: Colors.white,
dimColor: Colors.white,
controller: CropController(),
child: Image.memory(
base64.decode(
con.submodule['image'],
),
),
),
Positioned(
left: 0,
right: 0,
top: 200,
/// Rounded Container
child: Material(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(60),
topRight: Radius.circular(60),
),
child: Column(
children: <Widget>[
Container(
height: 225,
child: Column(
children: <Widget>[
/// Task Name and Number
Padding(
padding: const EdgeInsets.only(
top: 30, bottom: 20,),
child: Text(
widget.subTask['name'],
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
),
/// Short Description or Title
Padding(
padding: const EdgeInsets.only(
top: 10, bottom: 30,),
child:
Text(widget.subTask['short_description']),
),
/// Long Description
Flexible(
child: SingleChildScrollView(
child:
Text(widget.subTask['long_description']),
),
),
],
),
),
],
),
),
),
],
),
),
],
),
),
);
}
In the next version below, I've gotten rid of the Positioned widget, but now (marked with red arrows) I don't see how to get rid of the whitespace?
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: I10n.t('Playhouse'),
centerTitle: true,
automaticallyImplyLeading: false,
elevation: 0,
excludeHeaderSemantics: true,
),
endDrawer: const ScrapBookDrawer(),
body: Stack(
children: <Widget>[
Crop(
interactive: false,
backgroundColor: Colors.white,
dimColor: Colors.white,
controller: CropController(),
child: Image.memory(
base64.decode(
con.submodule['image'],
),
),
),
SafeArea(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Flexible(
child: Text(
widget.subTask['subName'],
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
const Flexible(
child: Text(
'Submodule description',
),
),
]),
const Spacer(),
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.white),
child: Column(
children: <Widget>[
/// Task Name and Number
Padding(
padding: const EdgeInsets.only(
top: 30,
bottom: 20,
),
child: Text(
widget.subTask['name'],
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
),
/// Short Description or Title
Padding(
padding: const EdgeInsets.only(
top: 10,
bottom: 30,
),
child: Text(widget.subTask['short_description']),
),
const SizedBox(height: 30),
Flexible(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Text(widget.subTask['long_description']),
),
),
],
),
),
)
],
),
)
],
),
);
}
Knowing that the second one is the closest to what you need, to remove the whitespace you only need to remove the bottom paddings and the sizedBox, because you are the one controlling the whitespaces with those widgets and set a width size to your container with long and short description, like this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: I10n.t('Playhouse'),
centerTitle: true,
automaticallyImplyLeading: false,
elevation: 0,
excludeHeaderSemantics: true,
),
endDrawer: const ScrapBookDrawer(),
body: Stack(
children: <Widget>[
Crop(
interactive: false,
backgroundColor: Colors.white,
dimColor: Colors.white,
controller: CropController(),
child: Image.memory(
base64.decode(
con.submodule['image'],
),
),
),
SafeArea(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Flexible(
child: Text(
widget.subTask['subName'],
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
const Flexible(
child: Text(
'Submodule description',
),
),
]),
const Spacer(),
Expanded(
child: Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.white),
child: Column(
children: <Widget>[
/// Task Name and Number
Padding(
padding: const EdgeInsets.only(
top: 30,
bottom: 20,
),
child: Text(
widget.subTask['name'],
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
),
/// Short Description or Title
Padding(
padding: const EdgeInsets.only(
top: 10,
),
child: Text(widget.subTask['short_description']),
),
Flexible(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Text(widget.subTask['long_description']),
),
),
],
),
),
)
],
),
)
],
),
);
}
I have an app that has a bottomappbar with a textformfield, but when the user tries to insert data there the keyboard hides the text being typed.
I tried "resizeToAvoidBottomInset: true" and didnt worked, also tried to put a SingleChildScrollView on the body of the app and got "RenderBox was not laid out: RenderRepaintBoundary#32f0a relayoutBoundary=up1 NEEDS-PAINT 'package:flutter/src/rendering/box.dart': Failed assertion: line 1785 pos 12: 'hasSize'".
I need a way to make the bottom part "expand" to the top of the keyboard when the user is typing or a way to show in the screen the text that is being typed.
How can I solve that? I have tried many ways and nothing seems to work.
The structure of my app is:
The code:
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text(
"xx",
),
//+ produtosList1[0].cod_produto),
actions: <Widget>[
Padding(
padding: EdgeInsets.only(right: 20.0),
child: GestureDetector(
onTap: () {
showDialog(
//dialog content
);
},
child: Icon(
Icons.search,
size: 26.0,
),
)),
],
),
drawer: Drawer(
elevation: 20.0,
child: ListView(padding: EdgeInsets.zero, children: <Widget>[
]
//appbar content
)),
body: Column(
children: [
Container(
color: Colors.blue,
child: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Column(
children: [
Row(children: [
Text(
"xxx",
style: TextStyle(
color: Colors.white,
),
overflow: TextOverflow.ellipsis),
]),
Row(children: [
Text(
"xxxx",
style: TextStyle(color: Colors.white),
)
])
],
)),
),
Container(
color: Colors.blue,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 8.0),
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Text("xxxx",
style: TextStyle(fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis),
),
Expanded(
flex: 1,
child: Text(
"xxxxx",
style: TextStyle(fontWeight: FontWeight.bold),
)),
Expanded(
flex: 4,
child: Text(
"xxxx",
style: TextStyle(fontWeight: FontWeight.bold),
)),
Expanded(
flex: 4,
child: Text(
"xxxx",
style: TextStyle(fontWeight: FontWeight.bold),
)),
],
)),
),
Divider(
height: 5.0,
),
Expanded(
child: ListView.builder(
itemCount: produtosList1.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 4.0, 0.0, 4.0),
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Text(
"xxxx"
),
),
Expanded(
flex: 1,
child: Text(
"xxxxx",
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Expanded(
flex: 4,
child: Text(
"xxxx",
style: TextStyle(fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis),
),
Expanded(
flex: 4,
child: Text(
"xxxxx",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 12),
overflow: TextOverflow.ellipsis),
),
],
),
);
}),
)
],
),
bottomNavigationBar: BottomAppBar(
child: Form(
child: Container(
height: 180.0,
color: Colors.blue[400],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(children: [
Container(
height: 50,
decoration: BoxDecoration(
color: Colors.white,
),
child: DropdownButtonFormField(
hint: Text('Choose '),
onChanged: (value) {
//dropdown values
},
items: newFuncionariosList,
),
),
Divider(
height: 6.0,
),
Row(
children: [
Expanded(
flex: 2,
child: TextFormField(
controller: _codprodController,
decoration: InputDecoration(
icon: Icon(Icons.add),
),
),
),
Expanded(
flex: 1,
child: ElevatedButton(
//button content
),
)
],
),
Divider(
height: 8.0,
),
Row(
children: [
Expanded(
flex: 6,
child: Text(produtoDesc == null ? "- - -" : produtoDesc,
overflow: TextOverflow.ellipsis)),
Expanded(
flex: 2,
child: TextFormField(
keyboardType: TextInputType.number,
controller: _qtdController,
decoration: InputDecoration(hintText: "qtd"),
),
),
Expanded(
flex: 2,
child: ElevatedButton(
//button content
))
],
)
]),
),
),
),
),
);
}
use MediaQuery.of(context).viewInsets.bottom and add it to your bottom navigation height
child: Form(
child: Container(
height: 180.0 + MediaQuery.of(context).viewInsets.bottom,
color: Colors.blue[400],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(children: [
Container(
height: 50,
decoration: BoxDecoration(
color: Colors.white,
),
),```
Fixed problem textfield was hidden by keyboard
new Scaffold(
appBar: new AppBar(
...
resizeToAvoidBottomPadding: true,
....
I want to add a button in the right side of the card,by default the button's color should be green and while tapping on it, there should be a popup box opened and the color of the button would turn to red.
1-The button's color should be green while the listView willed be plotted.
2-Then when I am tapping on the button, a dialog box should be opened with a input textField.
3-After I am inserting any text and pressing the submit button of the popup box, the entered text will be printed on the console.
4-Then the color of the button would turn to red from green.
As I am new to flutter, please help me to get this.
Here is my Code:
body: ListView.builder(
itemCount: patients.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(9.0),
child: SizedBox(
height: 120,
child: Card(
elevation: 5.0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Row(
children: [
Expanded(
flex: 3,
child: Container(
child: CircleAvatar(
backgroundImage: NetworkImage(patients[index].imgPath),
radius: 50.0,
),
),
),
Expanded(
flex: 5,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(patients[index].name, style: TextStyle(
fontSize: 23.0,
fontWeight: FontWeight.bold,
color: Colors.black87
),),
SizedBox(
height: 20,
),
Text(patients[index].completedSession.toString() +'/'+ patients[index].totalSession.toString(),
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Colors.black54
),),
],
),
),
),
Expanded(
flex: 2,
child: Container(
child: RaisedButton(
),
),
),
],
),
),
),
),
),
);
},
),
You should use CrossAxisAlignment.stretch for your Row:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() {
runApp(
MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: MyWidget(),
),
),
);
}
class MyWidget extends HookWidget {
#override
Widget build(BuildContext context) {
final pushed = useState(false);
return AspectRatio(
aspectRatio: 3,
child: Card(
elevation: 5.0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CircleAvatar(
backgroundImage: NetworkImage(
'https://upload.wikimedia.org/wikipedia/commons/5/54/RowanAtkinsonMar07.jpg',
),
radius: 50.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Mister B.',
style: TextStyle(
fontSize: 23.0,
fontWeight: FontWeight.bold,
color: Colors.black87),
),
SizedBox(
height: 20,
),
Text(
'0/42',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Colors.black54),
),
],
),
),
RaisedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
pushed.value ? 'Button unpushed' : 'Button pushed'),
),
);
pushed.value = !pushed.value;
},
color: pushed.value ? Colors.red : Colors.green,
child: Text('OK'),
),
],
),
),
),
);
}
}
So I am trying to learn BLoC pattern and apply it to my weather app. The thing is the UI is kinda big, there are a lot of Columns and Rows. So I'm confused about where to put BlocBuilder.
Or maybe this whole Container should be returned by one BLoC event? This is basically the main and only screen of the app for now.
class WeatherMainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: kBackgroundColor,
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
margin: EdgeInsets.only(
bottom: 30,
top: 15,
left: 30,
right: 30,
),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Today 28. apr',
style: TextStyle(
color: kAccentColor,
fontSize: 18,
fontWeight: FontWeight.w400,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
MenuSearchButton(
boxSize: 60,
onPressed: () => print('search pressed'),
content: Icon(
Icons.search,
color: Colors.white,
size: 30,
),
),
MenuSearchButton(
boxSize: 60,
onPressed: () => print('menu pressed'),
content: Icon(
Icons.menu,
color: Colors.white,
size: 30,
),
),
],
),
],
),
Container(
padding: EdgeInsets.symmetric(vertical: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'16',
style: TextStyle(
color: Colors.white,
fontSize: 100,
),
),
Container(
padding: EdgeInsets.only(left: 10.0),
child: Row(
children: <Widget>[
Icon(
Icons.cloud,
color: Colors.white,
size: 25.0,
),
SizedBox(width: 15.0),
Text(
'Raining',
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
],
),
),
],
),
),
],
),
),
Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(
bottom: 30,
top: 15,
left: 60,
right: 60,
),
child: Text(
'Put on your coat, and don\'t forget the umbrella.',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
Divider(
color: kAccentColor,
),
Container(
margin: EdgeInsets.only(
bottom: 30,
top: 15,
left: 30,
right: 30,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
DetailedCard(
header: 'HUMIDITY',
headerColor: kAccentColor,
text: '87%',
textColor: Colors.white,
),
DetailedCard(
header: 'WIND M/S',
headerColor: kAccentColor,
text: '4,1',
textColor: Colors.white,
),
DetailedCard(
header: 'FEELS LIKE',
headerColor: kAccentColor,
text: '18',
textColor: Colors.white,
),
],
),
),
],
),
],
),
),
);
}
}
Just separate your UI based on different possible states. Then use BlocBuilder and check states. Finally render UI for respective state.
You can check this video
You can definitely have multiple BlocBuilder. To make it simpler, let's take a look at this Counter example app.
I put this one wrapping a Column with 2 Text Widgets even though the only one supposed to be changing is the second Text Widget. (Not a good thing)
class CounterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Center(
child: Column(
children: <Widget>[
Text(
'You have pressed this many',
style: TextStyle(fontSize: 24.0),
),
Text(
'$count',
style: TextStyle(fontSize: 24.0),
),
],
),
);
},
),
);
}
}
In this snippet, I put the BlocBuilder pricisely wrapping the only Widget that is changing on state changes. And if you ask Where in my code should I put BLoC builder? This approach is better because there won't be unnecessary Widget rebuilds happen.
class CounterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
children: <Widget>[
Text(
'You have pressed this many',
style: TextStyle(fontSize: 24.0),
),
BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Text(
'$count',
style: TextStyle(fontSize: 24.0),
);
},
),
],
),
),
);
}
}
And for can I have more of them?, absolutely.
class CounterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return AppBar(title: Text('$count'));
},
),
body: Center(
child: Column(
children: <Widget>[
Text(
'You have pressed this many',
style: TextStyle(fontSize: 24.0),
),
BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Text(
'$count',
style: TextStyle(fontSize: 24.0),
);
},
),
],
),
),
);
}
}
I am struggling to understand how to layout my widget so that they are occupying the entire yellow space. More specifically I would like that the "hero' and the "boss" widget expand to occupy the available space on screen (excluding the keyboard)
My current code achieve the following result
I would like to get the following result below
Here is my code. I have used resizeToAvoidBottomInset: true to ensure the widget is resized with the keyboard popping up
Widget build(BuildContext context) {
return Container(
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: kColorPrimary,
appBar: AppBar(
backgroundColor: kColorPrimaryLight,
title: Text('Time to Spell'),
),
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Container(
height: 100,
color: Colors.amberAccent,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Text(
result,
textAlign: TextAlign.center,
style: kTitleTextStyle,
),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.blue,
child: Text(
'Timer: 2:00',
textAlign: TextAlign.center,
),
),
),
],
),
Row(
children: <Widget>[
Expanded(
child: Container(
height: 279,
color: Colors.purple,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.white,
child: Text(
'Hero',
textAlign: TextAlign.center,
),
),
),
),
),
Expanded(
child: Container(
height: 279,
color: Colors.greenAccent,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.white,
child: Text(
'Boss',
textAlign: TextAlign.center,
),
),
),
),
),
],
),
],
),
),
),
],
),
),
))));
}
Try this. I have added comments as well. Feel free to comment if anything is not clear.
#override
Widget build(BuildContext context) {
return SafeArea(
child: Container(
color: Colors.amberAccent,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Text(
"Click start",
textAlign: TextAlign.center,
),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.blue,
child: Text(
'Timer: 2:00',
textAlign: TextAlign.center,
),
),
),
],
),
Expanded(
child: Row(
children: <Widget>[
//child 1 of row takes half of the space
Expanded(
child: Column(
children: <Widget>[
//expanding the container to the bottom
Expanded(
child: Container(
//maximizing the width of the container
width: double.infinity,
color: Colors.purple,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.white,
child: Text(
'Hero',
textAlign: TextAlign.center,
),
),
),
),
),
],
),
),
//child 2 of row takes half of the space
Expanded(
child: Column(
children: <Widget>[
//expanding the container to the bottom
Expanded(
child: Container(
//maximizing the width of the container
width: double.infinity,
color: Colors.greenAccent,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.white,
child: Text(
'Boss',
textAlign: TextAlign.center,
),
),
),
),
),
],
),
),
],
),
),
],
),
),
);
}