Flutter - Set focus to another TextFormField in onFieldSubmitted - dart

I'm working with subsequent TextFormFields in an AlertDialog where the submit of an input should set the focus on the next input. I'm currently trying to achieve this using the following code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MainScreen()
);
}
}
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => new _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
final TextEditingController _firstFieldController = new TextEditingController();
final TextEditingController _secondFieldController = new TextEditingController();
FocusNode _focusNode ;
#override
void initState() {
super.initState();
_focusNode = new FocusNode();
}
#override
void dispose() {
_focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var firstField = new TextFormField(
controller: _firstFieldController,
keyboardType: TextInputType.phone,
decoration: new InputDecoration(
labelText: 'First field',
contentPadding: new EdgeInsets.fromLTRB(.0, 8.0, .0, 4.0),
counterText: ' '
),
onFieldSubmitted: (String textInput) {
FocusScope.of(context).requestFocus(_focusNode);
},
);
var secondField = new TextFormField(
focusNode: _focusNode,
controller: _secondFieldController,
keyboardType: TextInputType.phone,
decoration: new InputDecoration(
labelText: 'Second field',
contentPadding: new EdgeInsets.fromLTRB(.0, 8.0, .0, 4.0),
counterText: ' '
),
);
return new Scaffold(
appBar: new AppBar(
title: new Text('Main'),
),
body: new Center(
child: new Text('Hello from the main screen!'),
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
showDialog(
context: context,
child: new AlertDialog(
title: new Text('Form'),
content: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Container(
margin: new EdgeInsets.only(bottom: 4.0),
child: firstField
),
secondField
],
),
)
);
},
),
);
}
}
This is not setting the focus in the second field. However, if I close the dialog and open it again, the second field comes focused.
Can anyone help me with this?

That is because your AlertDialog needs to be in its own StatefulWidget. Your current code shows that your state, TextFields and AlertDialog are part of your MainScreen class, which means any updates has to happen first in the MainScreen context, while what you need is to have all your updates happen in the AlertDialog context instead.
TL;DR: Refactor your AlertDialog into its own StatefulWidget with TextFields, and FocusNode.

Related

Flutter - How to set focusNode from Parent to Child widget?

I have a Bottom Navigation in parent widget, and a few textfields in child widget. When user clicks on the navigation tab and if one of the textfields is empty, it will set focus on the particular textfields.
I am using the constructor method learnt from one of the developer however I couldn't get it work. It seems like I didn't pass over the context properly. I am not sure.
Anyone able to spot my mistakes or advise other methods which can achieve the same result?
login.dart
class Login extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return _LoginState();
}
}
class _LoginState extends State<Login> {
FocusNode focusNode;
Page1 focus;
#override
void initState() {
super.initState();
focusNode = new FocusNode();
focus = new Page1(focusNode: focusNode);
}
int currentBottomNavIndex = 0;
List<Widget> bottomNav = [
Page1(),
Page2(),
];
onTapped(int index) {
//if(textfield not empty) {
//setState(() {
//currentBottomNavIndex = index;
//});
//}else {
focus.setFocus(context);
//}
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
title: Text('Login Page'),
),
body: bottomNav[currentBottomNavIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTapped,
//onTap: requestFocus(context),
currentIndex: currentBottomNavIndex,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Page1"),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail),
title: Text('Page2'),
),
],
),
);
}
}
page1.dart
class Page1 extends StatefulWidget {
final FocusNode focusNode;
const Page1({Key key, this.focusNode}) : super(key: key);
void setFocus(BuildContext context) {
print("$focusNode requestFocus...");
FocusScope.of(context).requestFocus(focusNode);
}
#override
State<StatefulWidget> createState() {
return _Page1State();
}
}
class _Page1State extends State<Page1> {
TextEditingController name1 = TextEditingController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
nameApp(),
],
)
)
);
}
Widget nameApp(){
return Container(
margin: EdgeInsets.all(50.0),
//width: 185,
child: Center(
child: Row(
children: [
Container(
child: Text("Name :", style: TextStyle(fontSize: 15), ),
),
Container(
child: Flexible(
child: TextField(
focusNode: widget.focusNode,
controller: name1,
onTap: (){
name1.clear();
},
onChanged: (String str){
},
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 5),
hintText: "Full Name",
hintStyle: TextStyle(fontSize: 14),
),
),
),
),
]
)
)
);
}
}
When user click on the bottom tab, I expect to see the textfield is in focus however nothing happen.
I noticed the method in child widget has been called:
flutter: FocusNode#419f4 requestFocus...
flutter: FocusNode#419f4(FOCUSED) requestFocus...
however the textfield is still not focus.
I've create a simple sample project for this and its works for me just fine.
Please check out my solution:
The HomePage:
import 'package:flutter/material.dart';
import 'package:focus_node/widgets/MyInputWidget.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> {
FocusNode field1FocusNode = FocusNode(); //Create first FocusNode
FocusNode field2FocusNode = FocusNode(); //Create second FocusNode
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 35),
child: MyInputWidget(
focusNode: field1FocusNode, //Provide the first FocusNode in the constructor
hint: "Email",
onEditCompleted: (){
FocusScope.of(context).requestFocus(field2FocusNode); //Request focus
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 35),
child: MyInputWidget(
focusNode: field2FocusNode, //Provide the second FocusNode
hint: "Password",
onEditCompleted: (){
FocusScope.of(context).requestFocus(field1FocusNode); //Request focus
},
),
)
],
),
),
);
}
}
The Custom Widget required focus:
class MyInputWidget extends StatefulWidget {
final FocusNode focusNode;
final String hint;
final VoidCallback onEditCompleted;
MyInputWidget({this.focusNode, this.hint, this.onEditCompleted});
#override
_MyInputWidgetState createState() => _MyInputWidgetState();
}
class _MyInputWidgetState extends State<MyInputWidget> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8),
child: TextField(
focusNode: widget.focusNode, //The FocusNode provided by the parent widget
decoration: InputDecoration(
hintText: widget.hint
),
onEditingComplete: widget.onEditCompleted,
),
);
}
}
Hope this helps.

Using tabbar with animated list results in duplicate global key error

I am trying to implement Flutter's Tab Bar with 3 tabs and an AnimatedList inside those tabs. I want to use the same list and filter the list according to each tab (past tasks, today's tasks, and future tasks), however during my implementation of the tab bar together with the animatedlist I am getting an error regarding a duplicate global key in the widget tree. https://pastebin.com/iAW6DH9m . What would be the best way to deal with this error? Thank you for any help.
edit: I tried using this method to fix this. Multiple widgets used the same GlobalKey while it did fix my error I was then unable to access "currentstate" method on the key to be able to add more items to the list. I then tried a similar method using using GlobalKey and it resulted in a similar error of duplicate global keys.
This is my tab bar implementation
import 'package:flutter/material.dart';
import 'search_widget.dart';
import 'animatedlist_widget.dart';
class Dashboard extends StatefulWidget {
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
centerTitle: true,
actions: <Widget>[
new IconButton(icon: new Icon(Icons.grid_on), onPressed: null)
],
title: new Text('Dashboard'),
elevation: 0,
),
floatingActionButton: new FloatingActionButton(
onPressed: () {
_onFabPress(context);
},
child: new Icon(Icons.add)),
body: Scaffold(
appBar: new SearchWidget(
onPressed: () => print('implement search'),
icon: Icons.search,
title: 'Search',
preferredSize: Size.fromHeight(50.0),
),
body: DefaultTabController(
length: 3,
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: Container(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: new TabBar(
unselectedLabelColor: Colors.black45,
labelColor: Colors.white,
indicator: CustomTabIndicator(),
tabs: <Widget>[
new Tab(text: "Past"),
new Tab(text: "Today"),
new Tab(text: "Future")
]),
),
),
),
body: new TabBarView(
children: <Widget>[
AnimatedTaskList(),
AnimatedTaskList(),
AnimatedTaskList()
],
)
),
),
),
);
}
void _onFabPress(context) {
AnimatedTaskList().addUser();
}
/*showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return Container(
child: new Wrap(children: <Widget>[
new TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter Task Title')),
new TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter Task Details',
)),
]));
});
}*/
}
class CustomTabIndicator extends Decoration {
#override
BoxPainter createBoxPainter([onChanged]) {
// TODO: implement createBoxPainter
return new _CustomPainter(this, onChanged);
}
}
class _CustomPainter extends BoxPainter {
final CustomTabIndicator decoration;
_CustomPainter(this.decoration, VoidCallback onChanged)
: assert(decoration != null),
super(onChanged);
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
// TODO: implement paint
assert(configuration != null);
assert(configuration.size != null);
final indicatorHeight = 30.0;
final Rect rect = Offset(
offset.dx, (configuration.size.height / 2) - indicatorHeight / 2) &
Size(configuration.size.width, indicatorHeight);
final Paint paint = Paint();
paint.color = Colors.blueAccent;
paint.style = PaintingStyle.fill;
canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(30)), paint);
}
}
This is my animatedlist class:
import 'package:flutter/material.dart';
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
class AnimatedTaskList extends StatefulWidget {
void addUser() {
int index = listData.length;
listData.add(
TaskModel(
taskTitle: "Grocery Shopping",
taskDetails: "Costco",
),
);
_listKey.currentState
.insertItem(index, duration: Duration(milliseconds: 500));
}
#override
_AnimatedTaskListState createState() => _AnimatedTaskListState();
}
class _AnimatedTaskListState extends State<AnimatedTaskList> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: SafeArea(
child: AnimatedList(
key: _listKey,
initialItemCount: listData.length,
itemBuilder:
(BuildContext context, int index, Animation animation) {
return Card(
child: FadeTransition(
opacity: animation,
child: ListTile(
title: Text(listData[index].taskTitle),
subtitle: Text(listData[index].taskDetails),
onLongPress: () {
//todo delete user
},
)));
})),
);
}
}
class TaskModel {
TaskModel({this.taskTitle, this.taskDetails});
String taskTitle;
String taskDetails;
}
List<TaskModel> listData = [
TaskModel(
taskTitle: "Linear Algebra",
taskDetails: "Chapter 4",
),
TaskModel(
taskTitle: "Physics",
taskDetails: "Chapter 9",
),
TaskModel(
taskTitle: "Software Construction",
taskDetails: "Architecture",
),
];
I fixed my issue by moving
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
into my _AnimatedTaskListState class, and adding a constructor and private key to my AnimatedTaskList class
final GlobalKey<AnimatedListState> _key;
AnimatedTaskList(this._key);
#override
_AnimatedTaskListState createState() => _AnimatedTaskListState(_key);
then in my tab bar implementation I changed it to reflect my new constructor
AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 1"));
AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 2"));
AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 3"));

How to read data from Flutter elements

I've the below code for entering some data, I do not know how to handle it!
i.e. What shall I write at the onPressed for the IconButton so that the data is read from all the elements (name, birthday, ...)? and how to display it in Dialog to check if read correctly?
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.deepOrange,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _value=false;
double _bodyHeight=0.0;
void onchange(bool value) {
setState(() {
_value = value;
this._bodyHeight = (value == true) ? 400.0 : 0.0;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey[500],
appBar: new AppBar(
title: new Text(widget.title),
),
body: new SingleChildScrollView(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Card(
child: new Container(
height: 50.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Text("Select me pls"),
new Switch(value: _value, onChanged: (bool value) => onchange(value)),
],
),
),
),
new Card(
child: new AnimatedContainer(
child: new ListView(
children: <Widget>[
new ListTile(
leading: const Icon(Icons.person),
title: new TextField(
decoration: new InputDecoration(
hintText: "Name",
),
),
),
new ListTile(
leading: const Icon(Icons.phone),
title: new TextField(
decoration: new InputDecoration(
hintText: "Phone",
),
),
),
new ListTile(
leading: const Icon(Icons.email),
title: new TextField(
decoration: new InputDecoration(
hintText: "Email",
),
),
),
const Divider(
height: 1.0,
),
new ListTile(
leading: const Icon(Icons.label),
title: const Text('Nick'),
subtitle: const Text('None'),
),
new ListTile(
leading: const Icon(Icons.today),
title: const Text('Birthday'),
subtitle: const Text('February 20, 1980'),
),
new IconButton(icon: const Icon(Icons.save),
onPressed: null
/*
*
* What shall I write here to read the data in the elements
*
*
*
* */
),
],
),
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 500),
height: _bodyHeight,
// color: Colors.red,
),
),
],
),
),
);
}
}
One way is that TextFields take a property called TextEditingController which allow you to access the value of the TextField.
And to show a dialog you can just call showDialog() function.
class TextFieldExample extends StatefulWidget {
#override
_TextFieldExampleState createState() => new _TextFieldExampleState();
}
class _TextFieldExampleState extends State<TextFieldExample> {
TextEditingController c1;
TextEditingController c2;
#override
void initState() {
c1 = new TextEditingController();
c2 = new TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new TextField (
controller: c1,
),
new TextField(
controller: c2,
),
new OutlineButton(onPressed: () {
showDialog(child: new AlertDialog(
content: new Text("You entered ${c1.text} ${c2.text} ")
),
context: context
);
},
child: new Text("Show Dialog"),
)
],
),
),
);
}
}

Show a text field dialog without being covered by keyboard?

I'm trying to create a SimpleDialog that allows the user to enter their name. But when it is displayed the dialog is half hidden by the on-screen keyboard:
How can I get the Dialog to be fully visible?
Edit: I find it strange that the homepage widget (FocusVisibilityDemo) recognises the reduced height and therefore adjusts the position of the 'Push Me' button to remain in the center. Unfortunately the dialog doesn't behave the same way.
Here is my code:
import 'package:flutter/material.dart';
class FocusVisibilityDemo extends StatefulWidget {
#override
_FocusVisibilityDemoState createState() => new _FocusVisibilityDemoState();
}
class _FocusVisibilityDemoState extends State<FocusVisibilityDemo> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Text Dialog Demo')),
body: new Center(
child: new RaisedButton(
onPressed: _showDialog,
child: new Text("Push Me"),
),
),
);
}
_showDialog() async {
await showDialog<String>(
context: context,
child: new AlertDialog(
contentPadding: const EdgeInsets.all(16.0),
content: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
autofocus: true,
decoration: new InputDecoration(
labelText: 'Full Name', hintText: 'eg. John Smith'),
),
)
],
),
actions: <Widget>[
new FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.pop(context);
}),
new FlatButton(
child: const Text('OPEN'),
onPressed: () {
Navigator.pop(context);
})
],
),
);
}
}
void main() {
runApp(new MaterialApp(home: new FocusVisibilityDemo()));
}
If your use case is to add multiple TextFields inside your Dialog so your main Form does not get crowded, I think it is better if you build something more customizable than AlertDialog and SimpleDialog as they are used for simple activities (confirmations, radios..etc).
Otherwise, why do you want to use a Dialog for a single TextField ?
When we add multiple TextFields we should be careful about our design choices since other people will interact with this view to fill in the data, in this case I prefer to use fullscreenDialog property of PageRoute class. I am not sure if SimpleDialog can be suitable for that in Flutter.
Here is a quick example on how to use a FullScreenDialog, I hope this help and you should be able to modify it the way you want:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new MyApp(),));
}
class MyApp extends StatefulWidget {
#override
MyAppState createState() => new MyAppState();
}
class MyAppState extends State<MyApp> {
FullScreenDialog _myDialog = new FullScreenDialog();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Fill this form"),
),
body: new Column(
children: <Widget>[
new TextField(controller: new TextEditingController(
text: "Add a single text field"),),
new Card(child: new ListTile(
title: new Text("Click to add your top 3 amazing skills"),
subtitle: new Text(
"${_myDialog._skillOne} ${_myDialog._skillTwo} ${_myDialog
._skillThree}"),
onTap: () {
Navigator.push(context, new MaterialPageRoute(
builder: (BuildContext context) => _myDialog,
fullscreenDialog: true,
));
},
),
),
],
)
);
}
}
class FullScreenDialog extends StatefulWidget {
String _skillOne = "You have";
String _skillTwo = "not Added";
String _skillThree = "any skills yet";
#override
FullScreenDialogState createState() => new FullScreenDialogState();
}
class FullScreenDialogState extends State<FullScreenDialog> {
TextEditingController _skillOneController = new TextEditingController();
TextEditingController _skillTwoController = new TextEditingController();
TextEditingController _skillThreeController = new TextEditingController();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Add your top 3 skills"),
),
body: new Padding(child: new ListView(
children: <Widget>[
new TextField(controller: _skillOneController,),
new TextField(controller: _skillTwoController,),
new TextField(controller: _skillThreeController,),
new Row(
children: <Widget>[
new Expanded(child: new RaisedButton(onPressed: () {
widget._skillThree = _skillThreeController.text;
widget._skillTwo = _skillTwoController.text;
widget._skillOne = _skillOneController.text;
Navigator.pop(context);
}, child: new Text("Save"),))
],
)
],
), padding: const EdgeInsets.symmetric(horizontal: 20.0),)
);
}
}
EDIT
After doing some research, it seems that this is a bug in the current Flutter version, the temporary fix is also documented in this issue.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new FocusVisibilityDemo()));
}
class FocusVisibilityDemo extends StatefulWidget {
#override
_FocusVisibilityDemoState createState() => new _FocusVisibilityDemoState();
}
class _FocusVisibilityDemoState extends State<FocusVisibilityDemo> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Text Dialog Demo')),
body: new Center(
child: new RaisedButton(
onPressed: _showDialog,
child: new Text("Push Me"),
),
),
);
}
_showDialog() async {
await showDialog<String>(
context: context,
child: new _SystemPadding(child: new AlertDialog(
contentPadding: const EdgeInsets.all(16.0),
content: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
autofocus: true,
decoration: new InputDecoration(
labelText: 'Full Name', hintText: 'eg. John Smith'),
),
)
],
),
actions: <Widget>[
new FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.pop(context);
}),
new FlatButton(
child: const Text('OPEN'),
onPressed: () {
Navigator.pop(context);
})
],
),),
);
}
}
class _SystemPadding extends StatelessWidget {
final Widget child;
_SystemPadding({Key key, this.child}) : super(key: key);
#override
Widget build(BuildContext context) {
var mediaQuery = MediaQuery.of(context);
return new AnimatedContainer(
padding: mediaQuery.viewInsets,
duration: const Duration(milliseconds: 300),
child: child);
}
}

Flutter - SingleChildScrollView interfering in columns

I created a screen that works well with the columns, but I needed to scroll because of the keyboard.
When you insert the SingleChildScrollView or the ListView attribute the MainAxisAlignment.spaceBetween, it no longer works.
Was there any solution for that?
Gif without the SingleChildScrollView the screen does not roll and the FloatingActionButton is at the bottom of the screen
Gif with SingleChildScrollView the screen roll and he FloatingActionButton is not in bottom of the screen
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(backgroundColor: new Color(0xFF26C6DA)),
body: new SingleChildScrollView(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
child: new Column(
children: <Widget>[
new TextField(
decoration: const InputDecoration(
labelText: "Description",
),
style: Theme.of(context).textTheme.title,
),
new TextField(
decoration: const InputDecoration(
labelText: "Description",
),
style: Theme.of(context).textTheme.title,
),
new TextField(
decoration: const InputDecoration(
labelText: "Description",
),
style: Theme.of(context).textTheme.title,
),
],
)
),
new Container(
margin: new EdgeInsets.only(bottom: 16.0),
child: new FloatingActionButton(
backgroundColor: new Color(0xFFE57373),
child: new Icon(Icons.check),
onPressed: (){}
),
)
],
),
)
);
}
}
I would recommend against using FloatingActionButton in the way you are doing here. FloatingActionButton should be used for actions that always need to always be on screen at all times. See the Material guidelines on button usage for suggestions on other button types that can be scrolled, like RaisedButton and FlatButton. You could use a RaisedButton here, but I think it would be better to make your screen a dialog and put the save action in the AppBar as I suggested in your other question.
If you do decide to use a RaisedButton or FlatButton, keep in mind that scrollables don't normally change their item spacing based on the size of their container. Instead of using MainAxisAlignment.spaceBetween, you could put some Padding around your RaisedButton to separate it from the TextField elements. This will ensure that they are spaced the same distance apart regardless of rotation, screen size, and regardless of whether the keyboard is up.
Follow the code below to register.
MainAxisAlignment.spaceBetween has been replaced with dynamic padding, depending on screen size.
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final ui.Size logicalSize = MediaQuery.of(context).size;
final double _height = logicalSize.height;
return new Scaffold(
appBar: new AppBar(backgroundColor: new Color(0xFF26C6DA)),
body: new SingleChildScrollView(
child: new Column(
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
height: 300.0,
child: new Column(
children: <Widget>[
new TextField(
decoration: const InputDecoration(
labelText: "Description",
),
style: Theme.of(context).textTheme.title,
),
new TextField(
decoration: const InputDecoration(
labelText: "Description",
),
style: Theme.of(context).textTheme.title,
),
new TextField(
decoration: const InputDecoration(
labelText: "Description",
),
style: Theme.of(context).textTheme.title,
),
],
)
),
new Container(
padding: new EdgeInsets.only(top: (_height - 450.0)),
margin: new EdgeInsets.only(bottom: 16.0),
child: new FloatingActionButton(
backgroundColor: new Color(0xFFE57373),
child: new Icon(Icons.check),
onPressed: (){}
),
)
],
),
)
);
}
}

Resources