Reputation: 4644
I'm trying to pin a Row
container that has a text field input and an adjacent icon button within a ScrollView
. Unfortunately, the Row
will stay at the bottom but not with the view of the screen so the user will have to scroll down to see the Row
container. How can I pin the bottom bar to the screen so that it is always at the bottom within the view of the screen and over the top of the other objects in the ScrollView
?
Bar UI:
class TextBarAtBottom extends StatelessWidget {
TextEditingController commentController = TextEditingController();
@override
Widget build(BuildContext context) {
return Row(children: [
// First child is TextInput
Expanded(
child: Container(
child: TextFormField(
autocorrect: false,
decoration: new InputDecoration(
labelText: "Some Text",
labelStyle: TextStyle(fontSize: 16.0, color: Colors.black),
fillColor: Colors.black,
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
),
),
)),
// Second child is button
IconButton(
icon: Icon(Icons.send),
iconSize: 16.0,
onPressed: () {},
)
]);
}
}
Screen UI:
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('App'),
),
body: SingleChildScrollView(
child: Column(mainAxisSize: MainAxisSize.min, children: [
Flexible(
fit: FlexFit.loose,
child: ExpandableTheme(
data: ExpandableThemeData(
iconColor: Colors.blue,
useInkWell: true,
animationDuration: const Duration(milliseconds: 500),
tapBodyToExpand: true,
tapHeaderToExpand: true,
tapBodyToCollapse: true,
hasIcon: true,
iconPlacement: ExpandablePanelIconPlacement.right),
child: ExpandablePanel(
header: Text(widget.postData.title,
style: TextStyle(fontSize: 24)),
collapsed: Text(widget.postData.text,
style: TextStyle(fontSize: 16),
softWrap: true,
maxLines: 10,
overflow: TextOverflow.ellipsis),
expanded: Text(widget.postData.text,
style: TextStyle(fontSize: 16),
softWrap: true)))),
// Second child is spacing
SizedBox(height: 16),
// Third child is list view of Cards that are populated from the request post
Flexible(
fit: FlexFit.loose,
child: Container(
child: FutureBuilder<Map<String, String>>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemExtent: 128.0,
itemBuilder: (BuildContext context, int index) {
return Card(Text('$index'));
});
} else if (snapshot.hasError) {
return Flex(direction: Axis.horizontal, children: [
Expanded(
child: new Container(),
)
]);
}
return CircularProgressIndicator();
},
),
)),
// Fourth child is text bar and send button
Flexible(fit: FlexFit.loose, child: TextBarAtBottom())
]))));
}
Upvotes: 4
Views: 4942
Reputation: 3383
The results in the screenshot look like this.
1. image at the top of the list
2. image at the bottom of the list
I modified your own code, like this below
MaterialApp(
title: 'App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Document Thread'),
),
body: Column(
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// First child is ExpandableTheme with minHeight of 0.5 screen but expandable to fill up as much screen as needed
Flexible(
fit: FlexFit.loose,
child: ExpandableTheme(
data: ExpandableThemeData(
iconColor: Colors.blue,
useInkWell: true,
animationDuration: const Duration(milliseconds: 500),
tapBodyToExpand: true,
tapHeaderToExpand: true,
tapBodyToCollapse: true,
hasIcon: true,
iconPlacement: ExpandablePanelIconPlacement.right
),
child: ExpandablePanel(
header: Text('Title',
style: TextStyle(fontSize: 24)),
collapsed: Text('Some text',
style: TextStyle(fontSize: 16),
softWrap: true,
maxLines: 10,
overflow: TextOverflow.ellipsis),
expanded: Text('widget.postData.docAbstract',
style: TextStyle(fontSize: 16),
softWrap: true)
)
),
),
// Second child is spacing
SizedBox(height: 16),
// Third child is max height of 0.5 screen with ListView of Card objects or Empty container
Flexible(
fit: FlexFit.loose,
child: Container(
//this is limiting your list not to scroll to the bottom
// constraints: BoxConstraints(
// minWidth: MediaQuery.of(context).size.width,
// maxHeight: MediaQuery.of(context).size.height / 2,
// ),
child: ListView.builder(
shrinkWrap: true,
itemCount: 15,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(90,0,0,10),
child: Text('Text $index'),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.thumb_up, color: Colors.green,),
onPressed: (){},
),
IconButton(
icon: Icon(Icons.thumb_down, color: Colors.red,),
onPressed: (){},
)
],
),
Divider(color: Colors.black12,)
],
);
},
),
),
),
// Fourth child is text bar and send button
]
)
),
),
TextBarAtBottom()
],
)
)
);
Upvotes: 1
Reputation: 4094
Check my code, it's having textfield at the bottom, and scrollview in the center.
Problem with your code is you are adding Textfield inside scrollview so always at the end of SingleChildScrollview.
Solution: Add your SingleChildScrollView inside column view with Expanded widget. and add your Textfield as the second child to Column widget. Now TextField will be at the bottom and the rest of the space will be taken by SingleChildScrollView.
import 'package:flutter/material.dart';
class Design extends StatefulWidget {
@override
_DesignState createState() => _DesignState();
}
class _DesignState extends State<Design> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextField at Bottom'),
),
body: Column(
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
Text('Flutter'),
],
),
),
),
Row(children: [
// First child is TextInput
Expanded(
child: Container(
child: TextFormField(
autocorrect: false,
decoration: new InputDecoration(
labelText: "Some Text",
labelStyle: TextStyle(fontSize: 16.0, color: Colors.black),
fillColor: Colors.black,
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
),
),
)),
// Second child is button
IconButton(
icon: Icon(Icons.send),
iconSize: 16.0,
onPressed: () {},
)
])
],
),
);
}
}
Upvotes: 5