Placing the cursor at the top left of a vertically stretched TextField? - flutter

I'm trying to make a page through which the user can create a note for my Notes app. This is my build method so far:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add Note"),
actions: <Widget>[
FlatButton(
child: Icon(
Icons.save,
color: Colors.white,
),
)
],
),
body: Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextField (
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "Title",
),
textCapitalization: TextCapitalization.words,
),
SizedBox(
height: 16,
),
Expanded(
child: Container(
child: TextField (
expands: true,
maxLines: 25,
decoration: InputDecoration(
border: OutlineInputBorder(),
),
),
),
)
],
),
),
)
);
}
This is how the page looks right now. How do I ensure that the cursor of the second TextField is placed at the top left and make said TextField stretch to fit the remaining height?

You should set the textAlign property of the TextField to start
textAlign: start
Edit:
based on #Andy_519's and community input please use the following
textAlignVertical: top

Related

In Flutter, when I click on the TextFormField I get an overflowed error

return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 50),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.only(top: 2),
child: FaIcon(
FontAwesomeIcons.compass,
color: orange,
size: 26,
),
),
],
),
),
SizedBox(
height: 25,
),
Text(
'The final step for exploration.',
style: homepageSubtitle,
),
SizedBox(
height: 50,
),
TextFormWidget(
onChanged: () => null,
controller: _usernameController,
labelText: 'Username',
hintText: 'example#gmail.com'),
TextFormWidget(
onChanged: () => (value) => _checkPassword(value),
controller: _passwordController,
labelText: 'Password',
hintText: 'Min. 8 characters'),
Here, I created the page you see in the image.
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 20,
),
child: TextFormField(
onChanged: onChanged(),
keyboardType: TextInputType.text,
controller: controller,
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
labelText: labelText,
hintText: hintText,
labelStyle: labelTextStyle,
hintStyle: hintTextStyle,
),
),
),
],
);
Here is the TextFormWidget I created.
If I click on a TextFormField to enter a value, I get an overflowed error. What should I wrap the widget I created or the place where I show this widget so that I don't get this error?
Because keyboard appear make your Column doesn't has enough space. Try wrap your Column with SingleChildScrollView:
SingleChildScrollView(
child : Column(
children: [
// your children
]
);
)
Set this in your Scaffold:
resizeToAvoidBottomInset: false,
When the keyboard opens you have less space on the screen, and now your Column doesn't fit in the space you have.
Simple solution is to wrap it with SingleChildScrollView which will make it scrollable and therefore remove overflow.
I've used sample code for the answer.
class DemoApp extends StatelessWidget {
const DemoApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: [
Container(
height: 600,
color: Colors.deepPurple,
),
TextFormField(
decoration: const InputDecoration(labelText: 'Username'),
),
TextFormField(
decoration: const InputDecoration(labelText: 'Password'),
)
],
),
),
);
}
}
Output:
Bottom Overflow Error:
There are 3 ways to solve this issue:
1. Adding resizeToAvoidBottomInset: false in Scaffold
If text fields are close to the bottom of the screen, this approach is not recommended because the keyboard can hide the text fields.
Scaffold(
resizeToAvoidBottomInset: false,
body: Column( ...
Output:
2. Wrap the Column widget within SingleChildScrollView
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
child: Column( ...
Output:
3. By changing the Column widget to the ListView widget
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(
children: [ ...
Output:

Flutter: How to make chat page bottom text bar

I am trying to make a chat page on flutter that looks like this: top bar + bottom textField and send button. I have been able to make both the top appbar and the textfield with the send button, but I have not been able to position the textfield at the bottom of the page successfully. I have tried things like Align and Expand, footer, BottomNavigationBar.
The current version of the code I have pasted only shows the top Appbar. If I take out the child Row and the send button, I have a textfield at the bottom of the page. For some reason, as soon as I add the child Row in order to be able to add the send button, the entire textfield does not show up on the screen. I would appreciate any help.
Note: Since I am trying to make a chat screen, I want the middle section to be scrollable (while the top and bottom remain), where I can later add the chat bubbles.
Screenshot of code because of the bad editing of code snippet
Continuation of code snippet
"""
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorConstant.whiteA700,
// Top bar
appBar: AppBar(
backgroundColor: ColorConstant.deepOrange300,
title: Text("Match's Name",style: AppStyle.textstyleinterregular15.copyWith(
fontSize: getFontSize(15))),
),
body: Column(
children: [
Expanded(child: SingleChildScrollView(
child: Column(
children: [
// Bubbles
],
),
),
),
Container(
height: 45,
width: double.infinity,
color: ColorConstant.whiteA700,
child: Row(children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: "Message...",
hintStyle: TextStyle(color: ColorConstant.bluegray100),
border: OutlineInputBorder(
borderSide: BorderSide(color: ColorConstant.bluegray100)
),
)
),
SizedBox(width: 15,),
// Send Button
FloatingActionButton(
onPressed: (){},
child: Icon(Icons.send,color: ColorConstant.whiteA700,size: 18,),
backgroundColor: ColorConstant.lightBlueA100,
elevation: 0,
),
]),
);
],
),
);
"""
Try this one...
bottomNavigationBar: Padding(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
height: 45,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: Row(
children: [
Expanded(
child: const TextField(
decoration: InputDecoration(
hintText: "Message...",
hintStyle: TextStyle(color: Colors.blue),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue)),
),
),
),
SizedBox(
width: 15,
),
// Send Button
MaterialButton(
color: Colors.red,
onPressed: () {},
child: Icon(
Icons.send,
color: Colors.white,
size: 18,
),
// backgroundColor: ColorConstant.lightBlueA100,
elevation: 0,
),
],
),
),
),
Wrap TextField with Expanded widget it'll work for you.

Flutter app expanding behind navigation bar

After hours of trial and error, I gave up.
Can somebody tell me why my app is expanding behind the virtual navigation buttons?
I tried swiching and inserting SafeArea, Flex, Expanded and so on but have not found which Widget is creating this behaviour.
My Code:
Widget build(BuildContext context) {
return Scaffold(
body: Column(
// direction: Axis.vertical,
children: [
Expanded(
flex: 1,
// child:Text("test text", style: TextStyle(backgroundColor: Colors.blue),)
child: Container(
// color: Colors.red,
alignment: Alignment.centerRight,
// padding: EdgeInsets.all(0),
child: TextField(
controller: _input_controller,
decoration: const InputDecoration(
focusedBorder: InputBorder.none,
border: InputBorder.none,
),
style: TextStyle(fontSize: _font_size),
textAlign: TextAlign.right,
showCursor: false,
readOnly: true,
// decoration: InputDecoration(labelText: "Enter your number"),
// keyboardType: TextInputType.number,
// inputFormatters: [
// // FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
// // FilteringTextInputFormatter.allow(RegExp(r"[.,']")),
// CardFormatter
// ], // Only numbers can be entered
)),
),
Expanded(
flex: 1,
// child:Text("test text", style: TextStyle(backgroundColor: Colors.blue),)
child: Container(
// color: Colors.red,
alignment: Alignment.centerRight,
// padding: EdgeInsets.all(0),
child: TextField(
controller: _answer_controller,
decoration: const InputDecoration(
focusedBorder: InputBorder.none,
border: InputBorder.none,
),
style: TextStyle(fontSize: _font_size),
textAlign: TextAlign.right,
showCursor: false,
readOnly: true,
// decoration: InputDecoration(labelText: "Enter your number"),
// keyboardType: TextInputType.number,
// inputFormatters: [
// // FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
// // FilteringTextInputFormatter.allow(RegExp(r"[.,']")),
// CardFormatter
// ], // Only numbers can be entered
)),
),
Expanded(
flex: 4,
// child:Text("test text", style: TextStyle(backgroundColor: Colors.green),)
child: GridView.count(
crossAxisCount: 4,
// padding: const EdgeInsets.all(0.2),
mainAxisSpacing: 0,
crossAxisSpacing: 0,
children: List.generate(
20,
(i) => Container(
child: TextButton(
onPressed: () {
print(i);
},
child: Text(i.toString()),
),
))),
)
],
),
);
} crossAxisSpacing: 0,
children: List.generate(
20,
(i) => Container(
child: TextButton(
onPressed: () {
print(i);
},
child: Text(i.toString()),
),
)),
),
)
],
),
),
),
),
);
}
The issue is your screen height doesn't cover the Full widget height. That's why the GridView is behind the screen. If you scroll down, you will get the rest of the button. You can play with childAspectRatio to reduce the height.
If you like to have everything in withing screen, you can use Stack and LayoutBuilder to measure the screen size. But it is nice to have scroll-effect for small screen but I will prefer full scroll body, In that case you can use SingleChildScrollView on body and NeverScrollableScrollPhysics() on gridView.
You can check my repo I was testing year ago.
As always, when you ask, you get the answer shortly after by yourself (with a hint from Chance)
Putting GridView inside Expanded causes this behaviour, if you put GridView inside a Container you get an assertion error, add shrinkWrap: true to the Constructor of GridView and everything works as expected:
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(flex: 1, child: Text("Text1")),
Expanded(flex: 1, child: Text("Text1")),
Container(
child: GridView.count(
crossAxisCount: 4,
mainAxisSpacing: 0,
crossAxisSpacing: 0,
shrinkWrap: true,
children: List.generate(
20,
(i) => Container(
child: TextButton(
onPressed: () {
print(i);
},
child: Text(i.toString()),
),
))),
)
],
));
}

Flutter: Flexible column children inside a SingleChildScrollView

I have an app screen with a title on the top, a few buttons on the bottom, and an input form in the rest of the center.
What I'm trying to achieve: I want the input form children (i.e. the different input fields) to be spread out on the vertical axis to fill the available space.
When a the keyboard is open and occupies ~40% of the screen, I want all the flexible space between the input fields to shrink, and if thats not enough to show everything, I want the "center" part to be scrollable, so users can find the rest of the input fields, now hiding 'below'.
I'm using a combination of SingleChildScrollView with a child Column.
The widget tree (simplified..) looks like this:
return Scaffold(
body: SafeArea(
child: Column(
children: [
Text('Title',style: TextStyle(fontSize: 32),),
Expanded(
child: Container(
color: Colors.redAccent,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextFormField(decoration: InputDecoration(labelText: 'Firstname'),),
TextFormField(decoration: InputDecoration(labelText: 'Firstname'),),
TextFormField(decoration: InputDecoration(labelText: 'Firstname'),),
TextFormField(decoration: InputDecoration(labelText: 'Firstname'),),
TextFormField(decoration: InputDecoration(labelText: 'Firstname'),),
TextFormField(decoration: InputDecoration(labelText: 'Firstname'),),
TextFormField(decoration: InputDecoration(labelText: 'Firstname'),),
TextFormField(decoration: InputDecoration(labelText: 'Firstname'),),
],),),),),
ElevatedButton(onPressed: () => null, child: Text('Next')),
],),),);
When the keyboard is open, the above code results in this, which shrinks the Expanded red container, and is scrollable, so it is the desired result:
However, when the keyboard is closed I get this:
So in the non-keyboard layout, I want the input fields to be spread out evenly and take the entire red space.
I've tried all sorts of combinations by wrapping the children with Expanded and Flexible, add Flexible children in between the input fields, and what not.
Could not achieve the desired state. only getting Layout errors.
I would like to suggest you to use an AppBar, this way you already have the height to subtract!
Your mistake derives from not setting a Container with a fixed height.
That's my solution:
#override
Widget build(BuildContext context) {
var deviceHeight = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
elevation: 0,
centerTitle: true,
backgroundColor: Colors.white,
title: Text(
'Title',
style: TextStyle(fontSize: 32, color: Colors.black87),
),
),
body: SingleChildScrollView(
child: Container(
height: deviceHeight - (kToolbarHeight + 32), // 32 => Button height
child: Column(
children: [
Expanded(
child: Container(
color: Colors.redAccent,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Firstname'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Firstname'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Firstname'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Firstname'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Firstname'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Firstname'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Firstname'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Firstname'),
),
],
),
),
),
),
ElevatedButton(onPressed: () => null, child: Text('Next')),
],
),
),
),
);
}

flutter - How can I make my appbar act as material search widget while being flexible app bar

I want my appbar to act as a fixed appbar when it's scrolled down, or user's searching something.
SliverAppBar(
title: new TextField(
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
hintText: '검색',
),
),
),
But I want to draw it as a flexible appbar when it's scrolled up and user's not searching.
flexibleSpace: new FlexibleSpaceBar(
centerTitle: false,
title: new TextField(
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
hintText: '검색',
),
),
background: Stack(
fit: StackFit.expand,
children: <Widget>[
SizedBox(
height: 256.0,
child: Container(
child: Padding(
padding: const EdgeInsets.only(top: 24.0),
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: FlutterLogo(
size: 64.0,
),
),
Padding(padding: const EdgeInsets.only(bottom: 24.0)),
ListTile(
title: Text('Some Text'),
),
],
),
),
),
),
// This gradient ensures that the toolbar icons are distinct
// against the background image.
],
),
),
Search field is transformed to top-right little bit when scrolled up with second approach.
The effect can be achieved by moving title content to another SliverList.
Remove flexibleSpace from SliverAppBar, and move contents of flexibleSpace.background to SliverList before the SliverAppBar.
Example:
#override
Widget build(BuildContext context) {
return new CustomScrollView(
slivers: <Widget>[
new SliverList(
delegate: new SliverChildListDelegate(<Widget>[
Align(
alignment: Alignment.topLeft,
child: FlutterLogo(size: 64.0),
),
ListTile(
title: Text('Some Text'),
),
ListTile(),
])),
new SliverAppBar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0.0,
automaticallyImplyLeading: false,
pinned: true,
floating: false,
title: new TextField(
focusNode: _searchFocusNode,
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
suffixIcon: Icon(Icons.search),
hintText: '검색',
),
),
),
new SliverList(
delegate: new SliverChildListDelegate(List.generate(
100,
(i) => ListTile(
title: Text('Scroll'),
)).toList()),
),
],
);
}
I've written a getMaterialSerach(); method in this gist which has the exact material search view you need. just add getMaterialSearch() from this in your appBar: widget like below.
here is the gist for getMaterialSearch();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: getMaterialSearchBar(),
body: Center(
child: Container(),
),
);
}
Here's my code with a TextField and Tabs in a SliverAppBar:
NestedScrollView(
controller: model.mainScrollController,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
/// https://github.com/flutter/flutter/issues/54059
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
automaticallyImplyLeading: false,
pinned: true,
floating: true,
snap: true,
expandedHeight: 100,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
children: [
BackButton(
color: Colors.white,
),
Flexible(
child: TextField(
controller: model.searchController,
decoration: InputDecoration(
focusColor: Colors.blueAccent,
hoverColor: Colors.blueAccent,
fillColor: Colors.white,
filled: true,
isDense: true,
prefixIconConstraints: BoxConstraints(maxHeight: 24, maxWidth: 48),
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Icon(Icons.search_outlined),
),
hintText: 'Search...'),
),
),
IconButton(
icon: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
ExtendedNavigator.named('topNav').push(Routes.newExerciseView);
},
tooltip: 'Create Exercise',
),
],
),
),
),
bottom: TabBar(
labelPadding: EdgeInsets.only(bottom: 8),
indicatorWeight: 3,
indicatorSize: TabBarIndicatorSize.label,
tabs: [
Text('All'),
Text('Custom'),
Text('Favorites'),
],
),
),
),
];
},
body: TabBarView(
children: [
AllExercises(),
CustomExercises(),
FavoriteExercises(),
]),
),
There's a few key pieces that make this work right, the first being that you need to use the SliverAppBar's flexibleSpace property to add your search widget. If you try adding it in the title property, the TextField just get's squished but never goes off screen.
Second, make sure that the collapseMode of the FlexibleSpaceBar is set to CollapseMode.pin. This makes it so that the contents of the flexibleSpace scroll off screen and don't change size.
And finally, set pinned on the sliverAppBar to true so the TabBar sticks.