Black
Black

Reputation: 4644

Pin Row to bottom of Scroll View on top of other UI widgets

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())
            ]))));
  }

Screenshot

Upvotes: 4

Views: 4942

Answers (2)

Henok
Henok

Reputation: 3383

The results in the screenshot look like this.

1. image at the top of the list

top image

2. image at the bottom of the list

bottom image 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

MSARKrish
MSARKrish

Reputation: 4094

OutPut Screenshot

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

Related Questions