问题
I need some help with this layout :)
The Layout contains a BottomNavigationBar
. The body consists of a Container
at the top (which serves as some kind of header) and a Textfield
below the Container
. The Textfield
should expand to fill the remaining space. As soon as the user enters enough lines so that the text doesn't fit on the screen, the whole body (Textfield
and Container
) should become scrollable
.
So it's basically a note app with a Header
(the Container
part) and multiple Tabs
for Note taking.
This is the solution so far:
Scaffold buildBody(BuildContext context) {
return Scaffold(
appBar: AppBar(...),
body: _buildScaffoldBody(),
bottomNavigationBar: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AnimatedCrossFade(
firstChild: Material(
color: Theme.of(context).primaryColor,
child: TabBar(
controller: _tabController,
tabs: _tabNames,
onTap: (int) {
...
},
),
),
secondChild: Container(),
crossFadeState: _screen == 0
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
duration: const Duration(milliseconds: 300),
),
],
),
);
}
Widget _buildScaffoldBody() {
return LayoutBuilder(
builder: (context, constraint) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
Container(
height: 100,
alignment: Alignment.center,
color: Colors.green,
child: Text("Header"),
),
Expanded( //This is probably the cause
child: TabBarView( //of the exception
controller: _tabController,
children: <Widget>[
TextField(
expands: true,
maxLines: null,
decoration: InputDecoration(
fillColor: Colors.blue[200], filled: true),
),
TextField(
expands: true,
maxLines: null,
decoration: InputDecoration(
fillColor: Colors.blue[200], filled: true),
),
TextField(
expands: true,
maxLines: null,
decoration: InputDecoration(
fillColor: Colors.blue[200], filled: true),
),
]
)
)
],
),
),
),
);
},
);
But it's throwing an exception.I tried replacing Expanded
with a Container
and hardcoding height
and width
. If I do so the exception is not thrown, but the Textfield
is no longer expandable
and it doesn't scroll together with the Header
. It only scrolls within the Container
wrapping the Textfield
.
The following assertion was thrown during performLayout():
I/flutter ( 8080): RenderViewport does not support returning intrinsic dimensions.
I/flutter ( 8080): Calculating the intrinsic dimensions would require instantiating every child of the viewport, which
I/flutter ( 8080): defeats the point of viewports being lazy.
I/flutter ( 8080): If you are merely trying to shrink-wrap the viewport in the main axis direction, consider a
I/flutter ( 8080): RenderShrinkWrappingViewport render object (ShrinkWrappingViewport widget), which achieves that
I/flutter ( 8080): effect without implementing the intrinsic dimension API.
I/flutter ( 8080):
I/flutter ( 8080): The relevant error-causing widget was:
I/flutter ( 8080): IntrinsicHeight
回答1:
You just have some stuff misplaced. Try as much as you can to break apart your code into re-usable widgets. It helps to keep everything more organized.
Also, as a general rule, when you're trying to implement something commonly needed, chances are there will be a widget built for it already. In this case, you don't need to mess with the AnimatedCrossFade
etc... It's all built into the TabBarView
.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
home: TabBarScaffold(),
);
}
}
class TabBarScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
color: Colors.yellow,
home: DefaultTabController(
length: 3,
child: new Scaffold(
body: MyPages(),
bottomNavigationBar: MyTabs(),
),
),
);
}
}
class MyTabs extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TabBar(
tabs: [
Tab(
child: Text("Tab 1"),
),
Tab(
child: Text("Tab 2"),
),
Tab(
child: Text("Tab 3"),
),
],
labelColor: Colors.blue,
unselectedLabelColor: Colors.black,
indicatorSize: TabBarIndicatorSize.label,
indicatorPadding: EdgeInsets.all(5.0),
indicatorColor: Colors.red,
);
}
}
class MyPages extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TabBarView(
children: [
MyPageBody(
textBackgroundColor: Colors.blue[200],
),
MyPageBody(
textBackgroundColor: Colors.green[200],
),
MyPageBody(
textBackgroundColor: Colors.red[200],
),
],
);
}
}
class MyPageBody extends StatelessWidget {
final Color textBackgroundColor;
MyPageBody({this.textBackgroundColor});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraint) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
Container(
height: 100,
alignment: Alignment.center,
color: Colors.green,
child: Text("Header"),
),
Expanded(
child: TextField(
expands: true,
maxLines: null,
decoration: InputDecoration(
fillColor: textBackgroundColor, filled: true),
),
),
],
),
),
),
);
},
);
}
}
来源:https://stackoverflow.com/questions/59993861/flutter-using-expandable-textfield-with-bottomnavigationbar