Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter Expand/Occupy TextFormField to Fill Rest of Screen

I have a form with some TextFormField, and I want to expand the last TextFormField to occupy the rest of the screen. This last TextFormField can have multiple lines of text.

I haven't been able to achieve this, and have tried SizedBox.expand() and the Expanded widget, but no luck.

enter image description here

Below is the current code:

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: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(
              "What is this new classroom?"
            ),
            SizedBox(height: 8.0,),
            Expanded(
              child: Form(
              key: _formKey,
              child: ListView(
                padding: EdgeInsets.all(8.0),
                children: <Widget>[
                  Container(
                    padding: EdgeInsets.symmetric(vertical: 8.0),
                    child: TextFormField(
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                        labelText: "Classroom Name",
                        hintText: "What's name of the new classroom?",
                      ),
                    )
                  ),
                  SizedBox(height: 8.0,),
                  Container(
                    padding: EdgeInsets.symmetric(vertical: 8.0),
                    child: TextFormField(
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                        labelText: "Description",
                        hintText: "Description of the new classroom",
                      ),
                      //maxLines: 5,
                    ),
                  ),
                ]
              ),
            ),
          ),
        ],
      ),
    ),
    );
  }
}
like image 875
henrykodev Avatar asked Oct 28 '18 01:10

henrykodev


4 Answers

I edited your code a bit. But it didn't work. Please refer the below code. I will try to explain my understanding below the code.

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: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text("What is this new classroom?"),
            SizedBox(
              height: 8.0,
            ),
            Expanded(
                child: Form(
              key: _formKey,
              child: Column(children: <Widget>[
                Container(
                    padding: EdgeInsets.symmetric(vertical: 8.0),
                    child: TextFormField(
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                        labelText: "Classroom Name",
                        hintText: "What's name of the new classroom?",
                      ),
                    )),
                SizedBox(
                  height: 8.0,
                ),
                Expanded(
                  child: Container(
                    padding: EdgeInsets.symmetric(vertical: 8.0),
                    child: TextFormField(
                      maxLines: null,
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                        labelText: "Description",
                        hintText: "Description of the new classroom",
                      ),
                    ),
                  ),
                ),
              ]),
            )),
          ],
        ),
      ),
    );
  }
}

I inspected the view with your code. TextField which is inside TextFormField is not occupying the rest of the screen. So I edited to have TextField to have the rest of the screen. The above code does that. See the inspected view enter image description here

But the there is InputDecorator (which is child of our TextField) which draws the border line. In our case, it draws the border line based on content.

Possible workarounds could be:

  1. maxLines = null which will grow the TextField as the content groups. But initial view will be one line.
  2. Give fixed maxLines (as 10 or 20) which might look like occupying the screen. But it is not dynamic(doesn't change based on screen size / screen orientation)
like image 62
Dinesh Balasubramanian Avatar answered Jan 21 '23 19:01

Dinesh Balasubramanian


I found solution, maybe it can help someone. You can create Container or another widget with border same like TextField and make it expanded to fill whole Screen and above that with Stack widget put TextField with maxLines: null and without border.

[EDIT] It works also without Stack widget. Here is my code

Expanded(
                  child: Material(
                    borderRadius: BorderRadius.circular(10),                      
                    color: Colors.white,
                    child: TextField(                         
                      decoration: InputDecoration(
                        border: InputBorder.none,  
                        hintText: "Description",                         
                      ),
                      keyboardType: TextInputType.multiline,
                      maxLines: null,
                    ),
                  ),
                ),
like image 31
samuel98 Avatar answered Jan 21 '23 19:01

samuel98


You can also set contentPadding after widgets are rendered using addPostFrameCallback inside initState(). But you will have to calculate new height manually based on positions and heights of all above widgets.

Example:

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: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final _tffKey1 =
      GlobalKey();
  final _tffKey2 = GlobalKey();
  final _scaffoldKey = GlobalKey();
  final _textKey = GlobalKey();
  double _height = 0;

  //Set callback that will be called after widgets are rendered.
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      final RenderBox scaffoldKeyBox = _scaffoldKey.currentContext.findRenderObject();
      final RenderBox tffBox = _tffKey1.currentContext.findRenderObject();
      final RenderBox textBox = _textKey.currentContext.findRenderObject();
      final tffPos = tffBox.localToGlobal(Offset.zero);
      final textPos = textBox.localToGlobal(Offset.zero);

      //Calculate widget's height.
      _height = (scaffoldKeyBox.size.height -
          (tffBox.size.height + tffPos.dy) -
          (textBox.size.height + textPos.dy));

      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(
              "What is this new classroom?",
              key: _textKey,
            ),
            SizedBox(
              height: 8.0,
            ),
            Expanded(
              child: Form(
                key: _formKey,
                child:
                    ListView(padding: EdgeInsets.all(8.0), children: <Widget>[
                  Container(
                      padding: EdgeInsets.symmetric(vertical: 8.0),
                      child: TextFormField(
                        key: _tffKey1,
                        decoration: InputDecoration(
                          border: OutlineInputBorder(),
                          labelText: "Classroom Name",
                          hintText: "What's name of the new classroom?",
                        ),
                      )),
                  Container(
                    padding: EdgeInsets.symmetric(vertical: 8.0),
                    child: TextFormField(
                      key: _tffKey2,
                      decoration: InputDecoration(
                        contentPadding: EdgeInsets.only(left: 8, top: _height), // Set new height here
                        border: OutlineInputBorder(),
                        labelText: "Description",
                        hintText: "Description of the new classroom",
                      ),
                    ),
                  ),
                ]),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

example

like image 26
cora32 Avatar answered Jan 21 '23 19:01

cora32


I know it's a litte bit late, but the answer has been posted here. You just need the following

TextField(
  keyboardType: TextInputType.multiline,
  maxLines: whatever,
)
like image 33
Rami Alloush Avatar answered Jan 21 '23 19:01

Rami Alloush