EDIT: Let me clarify more the point I am struggling to achieve as it seems not to be clear. I already built a table with TextFields as you see in my code, and I tried the excel package and I tried several other packages, however nothing allows me to COPY the input from an external spreadsheet to my web app directly. The user will eventually have to manually type all the digital sample values one by one or they have to upload an external excel sheet to the site. These samples can be as big as several thousand samples in some cases, the only option is to copy them from their original spreadsheet or text source to the web app.
In my flutter web app, I want to get input from the users: they input time samples of any digital signal into a series of TextFields then the site will plot these samples and do some processing on them etc..
The problem is that digital signals are usually long and users will want to copy them from other text files or spreadsheets rather than manually typing them one by one in the browser.
Below is part of the code I used so far
class InputData extends StatefulWidget {
#override
State<InputData> createState() => _InputDataState();
}
class _InputDataState extends State<InputData> {
#override
Widget build(BuildContext context) {
return Material(
child:Scaffold(
body: Column(
children: <Widget>[
TitleBanner(),
Expanded(
child: Row(
children: [
Container(
height: 40,
width: 100,
child: Text('Sample Interval')),
Container(
height: 40,
width: 100,
child: TextField())
],
),
),
SizedBox(height: 100,),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0,0,0,0),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
decoration: BoxDecoration(border: Border.all() ),
height: 20,
width: 100,
child: Text('Samples')),
buildTextField(),
buildTextField(),
buildTextField(),
buildTextField(),
buildTextField(),
buildTextField(),
buildTextField(),
]
),
Column(
children: <Widget>[
Container(
decoration: BoxDecoration(border: Border.all() ),
height: 20,
width: 100,
child: Text('Amplitudes')),
buildTextField(),
buildTextField(),
buildTextField(),
buildTextField(),
buildTextField(),
buildTextField(),
buildTextField(),
]
)
],
),
),
)
],
),
)
);
}
Container buildTextField() {
return Container(
decoration: BoxDecoration(border: Border.all() ),
height: 20,
width: 100,
child: TextField(keyboardType: TextInputType.number,
decoration: kTextFieldDecoration
),);
}
}
This way the user can input the samples and amplitudes manually but they cannot copy them from another source. I tried using the excel package in flutter, but it seems to allow us to use a full excel spreadsheet and export it rather than integrating the cells inside the browser.
I tried also some other packages like sticky_headers but they seem to do good formatting only, without allowing a copy from external spreadsheet.
Is there a way to directly paste the values using this implementation?
If you don't need the pasted values to be editable the simplest solution would be to parse the input from a single TextField. Probably with a package like csv.
If you need a grid of input fields the solution gets more tricky. You need to listen to changes to each TextField, parse the data, and spread multicell input to the rest of the grid.
Each TextField will need a controller to set the text programmatically. They will also need an inputFormatter to make sure the receiving field only contains the content from one cell. You can use the formatter or a separate onChanged listener to parse the input and update the text for other cells in the grid.
I took the time to create a PoC of the grid solution here.
You need to use the Table widget, and inside its children use a TextField. I will give you the links I found and below I will paste the code from one of them:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root
// of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Table',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title:Text("GeeksforGeeks"),
backgroundColor: Colors.green,
),
body: Column(
children:<Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Table",textScaleFactor: 2,style: TextStyle(fontWeight:FontWeight.bold),),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Table(
// textDirection: TextDirection.rtl,
// defaultVerticalAlignment: TableCellVerticalAlignment.bottom,
// border:TableBorder.all(width: 2.0,color: Colors.red),
children: [
TableRow(
children: [
Text("Education",textScaleFactor: 1.5,),
Text("Institution name",textScaleFactor: 1.5),
Text("University",textScaleFactor: 1.5),
]
),
TableRow(
children: [
Text("B.Tech",textScaleFactor: 1.5),
Text("ABESEC",textScaleFactor: 1.5),
Text("AKTU",textScaleFactor: 1.5),
]
),
TableRow(
children: [
Text("12th",textScaleFactor: 1.5),
Text("Delhi Public School",textScaleFactor: 1.5),
Text("CBSE",textScaleFactor: 1.5),
]
),
TableRow(
children: [
Text("High School",textScaleFactor: 1.5),
Text("SFS",textScaleFactor: 1.5),
Text("ICSE",textScaleFactor: 1.5),
]
),
],
),
),
]
),
);
}
}
Table Widget in Flutter
How to manage textField controller for multiple dataRow in dataTable Flutter
It is also possible to use Excel:
Excel Pub Dev Package
Create an Excel Document in Flutter Youtube Guide
If I helped you, please don't forget to mark my answer as correct (tick).
If I understood you correctly, then you want to insert a default value into the TextField, you can do this with the following code:
Widget buildTextField(String initText) {
return Container(
decoration: BoxDecoration(border: Border.all()),
height: 20,
width: 100,
child: TextField(
controller: TextEditingController(text: initText),
keyboardType: TextInputType.number,
decoration: kTextFieldDecoration
),
);
}
Related
I'm creating a simple chatting app and I've added some authentication validation. During login or registration, when I get an error message from firebase, a widget pops up displaying the error. How do I make sure this widget goes away when I leave the screen? Right now if I press back it goes to the welcome screen and when I go back to the login screen, the error is still there. Here's the widget-
class ErrorMessage extends StatefulWidget {
#override
_ErrorMessageState createState() => _ErrorMessageState();
}
class _ErrorMessageState extends State<ErrorMessage> {
#override
Widget build(BuildContext context) {
if (error != null) {
return Container(
width: double.infinity,
color: Colors.amberAccent,
padding: EdgeInsets.all(8),
child: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 8),
child: Icon(Icons.error_outline),
),
Expanded(
child: Text(error, maxLines: 3),
),
Padding(
padding: EdgeInsets.only(left: 8),
child: IconButton(
icon: Icon(Icons.close),
onPressed: () {
setState(() {
error = null;
});
},
),
),
],
),
);
}
return SizedBox(
height: 0,
width: 0,
);
}
}
Instead of using a custom made widget just for displaying error/warning messages, I suggest using something like awesome_dialog which already has autoHide capability.
For example, this one will close itself after 4secs:
AwesomeDialog(
context: context,
animType: AnimType.LEFTSLIDE,
dismissOnTouchOutside: true,
dismissOnBackKeyPress: true,
headerAnimationLoop: true,
dialogType: DialogType.WARNING,
autoHide: Duration(seconds: 4),
body: Container(
child: Center(
child: Text(errorMessage, maxLines: 3),
),
)
I created an application in android studio to navigate from one screen to another.Here two stateless widgets are created as two screens and both contain a button to navigate pages each other.
However when i run the application a red screen is generated on my android phone I get an error saying
exception 'Column's children must not contain any null values, but a null value was found at index 0'.
I have provided my code below:
FIRST SCREEN
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("First Screen"),
),
body: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
center(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('assets/new 7wonders.jpg'),
fit: BoxFit.cover,
),
),
),
Text('New 7 Wonders',
style: TextStyle(fontSize: 40, fontStyle: FontStyle.italic),
),
RaisedButton(
child: Text("Bang Here"),
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => SecondScreen()));
},
color: Colors.red,
textColor: Colors.yellow,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.grey,
)
],
),
),
),
);
}
center({BoxDecoration decoration}) {}
}
SECOND SCREEN
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: RaisedButton(
child: Text("Go to First page"),
onPressed:() {
Navigator.pop(context);
},
),
);
}
}
Your center method should return a Widget, it is currently providing null to the Column.
Do this instead:
Widget center() {
// return a decorated box widget which gives you the decoration property
return Image(
image: AssetImage(
'assets/new 7wonders.jpg',),
fit: BoxFit.cover,
);
}
}
Then use in your Column like :
Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// call the center method which returns a Widget
center(),
Text(
'New 7 Wonders',
style: TextStyle(fontSize: 40, fontStyle: FontStyle.italic),
),
RaisedButton(
child: Text("Bang Here"),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondScreen()));
},
color: Colors.red,
textColor: Colors.yellow,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.grey,
)
],
),
),
),
you have to return any widget in center
center({BoxDecoration decoration}) {
return Container();
}
You tryed write Center instead center in line 24?
And in Center must be will return for example Containter()
In 24th line, you returned null value. You can implement the center method like this;
return Container();
Remove center use this
Container(
height: 100, // height and width according to your ui
width:100,
child:Image.asset(('assets/new7wonders.jpg',fit: BoxFit.cover,), // use for local image from asset and please change image name in code as well as in asset folder.there should not be space between image name .
),
I have several modalBottomSheet in my app to show, most of them have simple widget tree except only one that has 10 DropDownBottom widgets in it
Each one of them load about 200 items, each item is a widget consist of two main widgets a text and an image
when I Press
onPressed: () {
showModalBottomSheet(context: context, builder: picksWidget, isScrollControlled: true);
}
It take about 3 seconds to open load the modalBottomSheet and it just appear into the emulator without the sliding up animation, other modalBottomSheets in the app load perfectly fine, here is an example of the code I use.
import 'package:flutter/material.dart';
import 'package:myapp/data/picked.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:myapp/data/picks.dart';
import 'package:myapp/data/Icons.dart';
Widget buildPickerBottomSheet(BuildContext context) {
return Wrap(children: <Widget>[
PickerList(),
]);
}
class PickerList extends StatelessWidget {
const PickerList({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: Row(
children: <Widget>[
AutoSizeText(
'Pick ',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
maxLines: 1,
),
],
),
),
SizedBox(
height: 5,
),
PickerRow(
typeOne: 'vr',
typeTwo: 'er',
type: 'r',
),
PickerRow(
typeOne: 'vq',
typeTwo: 'eq',
type: 'q',
),
PickerRow(
typeOne: 'vw',
typeTwo: 'ew',
type: 'w',
),
PickerRow(
typeOne: 'vz',
typeTwo: 'ez',
type: 'z',
),
PickerRow(
typeOne: 'vy',
typeTwo: 'ey',
type: 'y',
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
child: Text('Add'),
onPressed: () async {
print('added');
},
),
RaisedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
)
],
),
SizedBox(
height: 50,
)
],
);
}
}
class PickerRow extends StatelessWidget {
final String typeOne;
final String typeTwo;
final String type;
PickerRow({#required this.typeOne, #required this.typeTwo, #required this.type});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
DropDownMenu(
pickType: typeOne,
),
Container(
width: 2,
height: 30,
color: Colors.blue,
),
Container(
height: 30,
width: 30,
child: Image(
image: AssetImage(AppIcons.types[type]),
),
),
Container(
width: 2,
height: 30,
color: Colors.red,
),
DropDownMenu(
pickType: typeTwo,
),
],
),
);
}
}
class DropDownMenu extends StatefulWidget {
//which position will this pick for
final String pickType;
DropDownMenu({#required this.pickType});
#override
_DropDownMenuState createState() => _DropDownMenuState();
}
class _DropDownMenuState extends State<DropDownMenu> {
//get a list of the picks to display in the drop down
static List<DropdownMenuItem> getDropDownItems() {
List<DropdownMenuItem<String>> dropDownItems = [];
for (int i = 0; i < Picks.picksNames.length; i++) {
String pick = Picks.picksNames[i];
var newItem = DropdownMenuItem(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
backgroundImage: AssetImage(AppIcons.picks[pick]),
radius: 15,
),
SizedBox(
width: 4,
),
AutoSizeText(
pick,
maxLines: 1,
),
],
),
value: pick,
);
dropDownItems.add(newItem);
}
return dropDownItems;
}
var items = getDropDownItems();
#override
Widget build(BuildContext context) {
String chosenItem = Picked.selection[widget.pickType];
return DropdownButton<String>(
value: chosenItem,
items: items,
onChanged: (value) {
setState(() {
chosenItem = value;
});
Picked.selection[widget.pickType] = value;
},
);
}
}
I am new to development in general so I appreciate if there is any resources on how to measure and improve performance on flutter apps.
Thanks.
It seems like your bottomModalSheet is too heavy. It loads only after the children have completed building.
Resize the images that you use inside the bottomModalSheet.
Usually the debug app is very slow compared to the release app.
Generate the apk of the app: flutter build apk --split-per-abi.
Install the generated apk build/app/outputs/apk/release/app-armeabi-v7a-release.apk and see if the problem still persists.
You should avoid rendering 2000 widgets at once.
First of all look at ListView widget and it's builder(). Try to refactor your code and put all your items inside this ListView. It will increase your performance a lot due to the reason that ListView.builder() allows you not to store all your widgets in memory all the time.
https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html
I have a long text and I need to show the scrollbar by default when the user enters my page.
Currently, the bars not shown until the user click over the text and this, not good behavior because the user could leave the page without notice that there is some unread text.
My code:
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Column(
children: <Widget>[
Image.asset(
"assets/images/logo.png",
height: 200.0,
),
SizedBox(
height: 40,
),
Expanded(
child: Scrollbar(
child: SingleChildScrollView(
child: Text("Long Text Here ...",
textDirection: TextDirection.rtl,
style: TextStyle(fontSize: 17.2),
),
),
),
),
SizedBox(
height: 50,
),
Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text("Continue"),
onPressed: () {
MaterialPageRoute route = MaterialPageRoute(
builder: (BuildContext context) => MainPage());
Navigator.of(context).push(route);
},
),
),
SizedBox(
width: 20.0,
),
RaisedButton(
child: Text("Close"),
onPressed: () {
exit(0);
},
),
],
)
],
),
),
),
);
}```
As of Flutter version 1.17, on Scrollbar you can set isAlwaysShown to true, but you must set the same controller for your Scrollbar and your SingleChildScrollView (and that applies to any other scrollable Widget as well).
Have in mind that, for the Scrollbar to be visible, there must be enough items to scroll. If there are not, the Scrollbar won't be shown.
Full working example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyWidget(),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Column(
children: <Widget>[
// ...
Expanded(
child: Scrollbar(
controller: _scrollController, // <---- Here, the controller
isAlwaysShown: true, // <---- Required
child: SingleChildScrollView(
controller: _scrollController, // <---- Same as the Scrollbar controller
child: Text(
"Long Text Here ...",
textDirection: TextDirection.rtl,
style: TextStyle(fontSize: 17.2),
),
),
),
),
// ...
],
),
),
),
);
}
}
As of v2.9.0-1.0, thumbVisiblity is the proper field to set.
Note you can set this globally for your app (or a certain subtree) using ScrollbarTheme:
return MaterialApp(
...
theme: ThemeData(
...
scrollbarTheme: ScrollbarThemeData(
thumbVisibility: MaterialStateProperty.all<bool>(true),
)
)
)
It's good to prefer themes for styling like this, so avoid doing more than once.
You'll still need to add a Scrollbar and Controller as described in other answers though.
Use draggable_scrollbar package. It provides a dragable scrollbar with option to make the scrollbar always visible. For example, you can use the following code
DraggableScrollbar.arrows(
alwaysVisibleScrollThumb: true, //use this to make scroll thumb always visible
labelTextBuilder: (double offset) => Text("${offset ~/ 100}"),
controller: myScrollController,
child: ListView.builder(
controller: myScrollController,
itemCount: 1000,
itemExtent: 100.0,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Material(
elevation: 4.0,
borderRadius: BorderRadius.circular(4.0),
color: Colors.purple[index % 9 * 100],
child: Center(
child: Text(index.toString()),
),
),
);
},
),
);
'isAlwaysShown' is deprecated and shouldn't be used. Use thumbVisibility instead.
Example:
Scrollbar(
controller: ScrollController(),
thumbVisibility: true,
child: SingleChildScrollView(
child: Column(
thumbVisibility make true for show always scroll bar for list in scrollbar widget
Scrollbar(thumbVisibility: true,)
You can change the scrollbartheme to set flag isAlwaysShown true
scrollbarTheme: const ScrollbarThemeData(isAlwaysShown: true)
I would like to create a view that has to have a Column with a scroll view (e.g. something like SingleChildScrollView) and a footer regardless of the screen size. If the screen is big enough, it will use the empty space between the scroll and the footer, if not, it will expand and only make the widget above the footer scrollable.
It's more or less like Listview with scrolling Footer at the bottom but with a diference that I want the keyboard to overflow the footer and it also should stay in place.
Something like
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(left: 30.0, right: 30.0, top: 80.0),
child: Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// Multiple widgets and form fields
],
),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 50.0),
child: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// Footer widgets
],
),
),
)
],
),
);
For those who were looking to implement just footer with scrollview in a simple way, below code might help :
Scaffold(
appBar: buildAppBar('Some cool appbar'),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
PackageCard(),
PackageCard(),
PackageCard(),
],
),
),
),
Container(
child: Text('Your super cool Footer'),
color: Colors.amber,
)
],
),
);
Visual representation:-
---Column
|
|---Expanded--
|-SingleChildScrollView (column /YOUR SCROLLABLE VIEW)
|
|-Container(YOUR FOOTER)
I used expanded with SinglechildScrollView over here
Even though the Rémi answer being right, I've actually found an easier way to achieve what I was looking for by just combining the LayoutBuilder with the IntrinsicHeight.
class ScrollableFooter extends StatelessWidget {
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: constraints.copyWith(
minHeight: constraints.maxHeight,
maxHeight: double.infinity,
),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
// Your body widgets here
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: // Your footer widget
),
),
],
),
),
),
);
});
}
}
How I solved this was to wrap the fixed Footer and The SingleChildScrollView in a Stack widget then align the Footer accordingly.
class ScrollableFooter extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
children: <Widget>[
// Your body widgets here
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: // Your fixed Footer here,
),
],
);
}
}
The accepted solution works in many cases, but it becomes tricky when you want to use something like a ListView because it can't provide an intrinsic height. I tried to find some different solution, and turns out I could, and it seems more flexible. I managed to solve this situation using slivers. Where the content is inside a sliver, and the footer is also inside a sliver.
Tip: Watch "The Boring Flutter Development Show, Ep. 12", which is all about slivers.
return Scaffold(
body: CustomScrollView(
shrinkWrap: true,
slivers: [
SliverToBoxAdapter(
child: Column(
children: [
//content widgets
],
),
),
SliverFillRemaining(
hasScrollBody: false,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
//footer widgets,
],
),
),
],
),
);
The difficulty is that Column and SingleChildScrollView have a hard time working together because one needs constraints and the other removes them.
The trick is to use a CustomMultiChildLayout and do the calculations yourself. Helped by MediaQuery to obtain the size of the keyboard, so that the footer can disappear to leave more room for the content.
Here's a reusable widget that does it for you:
class FooterLayout extends StatelessWidget {
const FooterLayout({
Key key,
#required this.body,
#required this.footer,
}) : super(key: key);
final Container body;
final Container footer;
#override
Widget build(BuildContext context) {
return CustomMultiChildLayout(
delegate: _FooterLayoutDelegate(MediaQuery.of(context).viewInsets),
children: <Widget>[
LayoutId(
id: _FooterLayout.body,
child: body,
),
LayoutId(
id: _FooterLayout.footer,
child: footer,
),
],
);
}
}
enum _FooterLayout {
footer,
body,
}
class _FooterLayoutDelegate extends MultiChildLayoutDelegate {
final EdgeInsets viewInsets;
_FooterLayoutDelegate(this.viewInsets);
#override
void performLayout(Size size) {
size = Size(size.width, size.height + viewInsets.bottom);
final footer =
layoutChild(_FooterLayout.footer, BoxConstraints.loose(size));
final bodyConstraints = BoxConstraints.tightFor(
height: size.height - max(footer.height, viewInsets.bottom),
width: size.width,
);
final body = layoutChild(_FooterLayout.body, bodyConstraints);
positionChild(_FooterLayout.body, Offset.zero);
positionChild(_FooterLayout.footer, Offset(0, body.height));
}
#override
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
return true;
}
}
Used as such:
FooterLayout(
body: body,
footer: footer,
),
Although the accepted answer seems to work on mobile devices, problems occur when the width is (much) bigger than the height. When that happens, the IntrinsicHeight acts like an AspectRatio, and the height increases so the footer is pushed off the screen.
I think the problem is with the definition used by the IntrinsicHeight of its internal workings:
... instead size itself to a more reasonable height.
I can confirm that #Rémi's solutions works also in those cases.
It deserves to be a standard widget provided by the Flutter framework.