I am currently new to flutter.I have a question.I have a simple login interface as:
Form(
key: _formKey,
child: ListView(
children: [
Center(
child: Icon(
Icons.face,
size: 128,
color: Theme.of(context).iconTheme.color,
),
),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 45),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? 'Enter username' : null,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
controller: usernameController,
decoration: InputDecoration(
hoverColor: Colors.red,
border: const OutlineInputBorder(
borderSide: BorderSide(),
borderRadius: BorderRadius.all(Radius.circular(50))),
hintText: 'Username',
hintStyle: TextStyle(
color: Theme.of(context).hintColor,
)),
textAlign: TextAlign.center,
),
),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 45, vertical: 25),
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
controller: passwordController,
obscureText: isSecured,
decoration: InputDecoration(
border: const OutlineInputBorder(
borderSide: BorderSide(),
borderRadius: BorderRadius.all(Radius.circular(50))),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.red),
borderRadius: BorderRadius.all(Radius.circular(60))),
hintText: 'Password',
suffixIcon: IconButton(
onPressed: () {
setState(() {
isSecured = !isSecured;
showpassWord = isSecured
? Icons.remove_red_eye
: Icons.visibility_off;
});
},
icon: Icon(showpassWord)),
hintStyle: TextStyle(color: Theme.of(context).hintColor),
),
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 85),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.secondary)),
onPressed: () {
if (_formKey.currentState!.validate()) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return LoginPage(
username: usernameController.text.toString(),
password: passwordController.text.toString(),
);
},
),
);
}
},
child: const Text(
'Login',
style: TextStyle(color: Colors.white),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Dont have account?'),
TextButton(
onPressed: () {},
child: const Text(
'SignUp',
style: TextStyle(
color: Colors.blue,
),
),
),
],
),
],
),
),
It works perfectly fine when using SizedBox to give some space between each TextField .Now the problem lies when I replace my SizedBox with a Spacer() widget.
After using Spacer instead of Sizedbox my code looks like:
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Theme.of(context).colorScheme.background,
title: Text('MyApp', style: Theme.of(context).textTheme.titleLarge),
actions: [
IconButton(
onPressed: () {},
icon: const Icon(
Icons.message,
),
),
IconButton(
onPressed: () {},
icon: const Icon(
Icons.settings,
),
)
],
),
body: Form(
key: _formKey,
child: ListView(
children: [
Center(
child: Icon(
Icons.face,
size: 128,
color: Theme.of(context).iconTheme.color,
),
),
const Spacer(
flex:1
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 45),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? 'Enter username' : null,
controller: usernameController,
),
),
const Spacer(
flex:1
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 45, vertical: 25),
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
controller: passwordController,
obscureText: isSecured,
decoration: InputDecoration(
hintText: 'Password',
suffixIcon: IconButton(
onPressed: () {
setState(() {
isSecured = !isSecured;
showpassWord = isSecured
? Icons.remove_red_eye
: Icons.visibility_off;
});
},
icon: Icon(showpassWord)),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 85),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return LoginPage(
username: usernameController.text.toString(),
password: passwordController.text.toString(),
);
},
),
);
}
},
child: const Text(
'Login',
style: TextStyle(color: Colors.white),
),
),
),
Row(
children: [
const Text('Dont have account?'),
TextButton(
onPressed: () {},
child: const Text(
'SignUp',
),
),
],
),
const Spacer(
flex: 2,
)
],
),
),
);
It gives me error as:
The following assertion was thrown while applying parent data.:
Incorrect use of ParentDataWidget.
The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type FlexParentData to a RenderObject, which has been set up to accept ParentData of incompatible type ParentData.
Usually, this means that the Expanded widget has the wrong ancestor RenderObjectWidget. Typically, Expanded widgets are placed directly inside Flex widgets.
The offending Expanded is currently placed inside a RepaintBoundary widget.
Why can’t I use spacer() inside SingleChildScrollView() or ListView()?
2
Answers
Spacer can not directly be used with SingleChildScrollView. In Layman’s terms, Spacer works by calculating the available space, distributing it amongst the children of a Row or Column and then filling up the remaining space. With SingleChildScrollView, the constraints are unbounded(infinite) because the content is supposed to be scrollable and Spacer can not perform its calculation, hence, the error it throws.
You can however use Spacer with SingleChildScrollView but you need to:
You can find this in Flutter’s official documentation site here.
A ListView or SingleChildScrollView widget will provide infinite height (Constraints){default on vertical} and
Spacer()
(which is an Expanded) widget also try to take as much space it can get. You can think of scrollable widget is offering infinite space andSpacer
is trying to access as much spacer it can get which is infinite. This is where error occurs.I will suggest checking layout/constraints