Automatically scroll multiline TextFormField when it extends the maxLines attribute - textfield

I'm implementing a TextFormField with the maxLines attribute set to 3. How can I make the TextFormField scroll down once the user starts with his fourth line? At the moment the cursor is just not visible anymore until the user scrolls down by hand. Is there a way to do this automatically?
This behaviour is actually featured in the flutter_gallery app in the 'Text fields' example. Just type a long text to the 'Live story' input until it reaches the fourth line.
The important parts of my code actually look like this:
import 'package:flutter/material.dart';
class TextFormFieldDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Form(
child: new TextFormField(
maxLines: 3,
),
),
);
}
}
So far I have found no workaround for this issue.
This issue affects both iOS and android.

Our team accomplished this by nesting some existing widgets:
// create the illusion of a beautifully scrolling text box
return new Container(
color: Colors.gray,
padding: new EdgeInsets.all(7.0),
child: new ConstrainedBox(
constraints: new BoxConstraints(
minWidth: _contextWidth(),
maxWidth: _contextWidth(),
minHeight: AppMeasurements.isLandscapePhone(context) ? 25.0 : 25.0,
maxHeight: 55.0,
),
child: new SingleChildScrollView(
scrollDirection: Axis.vertical,
reverse: true,
// here's the actual text box
child: new TextField(
keyboardType: TextInputType.multiline,
maxLines: null, //grow automatically
focusNode: mrFocus,
controller: _textController,
onSubmitted: currentIsComposing ? _handleSubmitted : null,
decoration: new InputDecoration.collapsed(
hintText: ''Please enter a lot of text',
),
),
// ends the actual text box
),
),
);
}
We had help from Darky to get widget ordering and the correct widgets to make it work.

This appears to be a missing feature in the Flutter Framework, I've filed a bug to get it resolved: https://github.com/flutter/flutter/issues/9365

You can use BoxConstraints and set maxHeight of your TextField
Container(
constraints: BoxConstraints(maxHeight: 100),
child: SingleChildScrollView(
child: TextField(
maxLines: null,
),
),
);

With the latest flutter 1.20.4, this text field will scroll when it rich the max hight.
Container(
constraints: BoxConstraints(maxHeight: 200),
child: TextField(
maxLines: null,
.......
)
)
If you are using the Textfield inside Raw or column wrap it in Expanded widget

I made it work like this. Hope it helps!
return new Card(
shape: RoundedRectangleBorder(
side: BorderSide(
color: Colors.deepPurpleAccent,
width: 1
),
borderRadius: BorderRadius.circular(3)
),
child: Container(
height: 50,
child: SingleChildScrollView(
child: TextField(
maxLines: null,
),
),
),
);

I solve this with ScrollController.
ScrollController textFieldScrollController = ScrollController();
TextField(
scrollController: textFieldScrollController,
keyboardType: TextInputType.multiline,
minLines: null,
maxLines: null,
onChanged: (value) {
textFieldScrollController.jumpTo(textFieldScrollController.position.maxScrollExtent);
},
),

Just Use TextFormField() widget & set minLines= and maxLines= .
The missing thing is here Scroll indicator.
TextFormField(
minLines: 3,
maxLines: 3,
key: _messageValueKey,
decoration: _inputDecoration,
),

Just add the 'reverse: true' to your Scrollable Widget, like so:
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: 130.0,
decoration: BoxDecoration(
border: Border.all(color: Colors.purpleAccent),
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
reverse: true,
child: Text(
'$message',
maxLines: 2,
style: TextStyle(fontSize: 30),
),
),
);
}
}

Related

Flutter Error Valid value range is empty: 0

This error keeps coming up after my CircularProgressIndicator() is finished running for some time. I have no idea, why is this error coming and how can i fix it. I read the answers for the similar question asked before and tried them, but nothing changed. I am not sure that is this caused by my ListView.builder() or something else.
Here's the code -
main.dart -
import 'package:flutter/material.dart';
import 'package:soccer_app/api_manager.dart';
import 'package:soccer_app/pagerbody.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SoccerApp(),
);
}
}
class SoccerApp extends StatefulWidget {
#override
_SoccerAppState createState() => _SoccerAppState();
}
class _SoccerAppState extends State<SoccerApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFAFAFA),
appBar: AppBar(
backgroundColor: Color(0xFFFAFAFA),
elevation: 0.5,
title: Text('SoccerBoard', style: TextStyle(color: Colors.black),),
centerTitle: true,
),
body: FutureBuilder(
future: SoccerApi().getAllMatches(),
builder: (context, snapshot) {
if(snapshot.hasData) {
return PageBody(snapshot.data);
}
else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
Pagebody.dart -
import 'package:flutter/material.dart';
import 'package:soccer_app/goal_stat.dart';
import 'package:soccer_app/matchtile.dart';
import 'package:soccer_app/soccermodel.dart';
import 'package:soccer_app/teamstats.dart';
Widget PageBody(List<SoccerMatch> allmatches){
return Column(
children: [
Expanded(
flex: 2,
child: Container(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 18.0, vertical: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
teamStat("Local Team", allmatches[0].home.logoUrl, allmatches[0].home.name),
goalStat(allmatches[0].fixture.status.elapsedTime, allmatches[0].goal.home, allmatches[0].goal.away),
teamStat("Visitor Team", allmatches[0].away.logoUrl, allmatches[0].away.name),
],
),
),
),
),
Expanded(
flex: 5,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Color(0xff4373d9),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40.0),
topRight: Radius.circular(40.0),
),
),
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"MATCHES",
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
),
),
Expanded(
child: ListView.builder(
itemCount: allmatches.length,
itemBuilder: (context, index){
return MatchTile(allmatches[index]);
},
),
),
],
),
),
),
),
],
);
}
And this is the error -
The following RangeError was thrown building FutureBuilder<List<SoccerMatch>>(dirty, state: _FutureBuilderState<List<SoccerMatch>>#2a0fc):
RangeError (index): Invalid value: Valid value range is empty: 0
The relevant error-causing widget was:
FutureBuilder<List<SoccerMatch>> file:///C:/Users/Hp/AndroidStudioProjects/soccer_app/lib/main.dart:36:13
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:177:60)
#1 PageBody (package:soccer_app/pagerbody.dart:19:50)
#2 _SoccerAppState.build.<anonymous closure> (package:soccer_app/main.dart:40:20)
#3 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:751:55)
#4 StatefulElement.build (package:flutter/src/widgets/framework.dart:4744:28)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (10805): Api service: {get: fixtures, parameters: [], errors: {required: At least one parameter is required.}, results: 0, paging: {current: 1, total: 1}, response: []}
the list coming from your API is empty thus
List<SoccerMatch> allmatches is empty
and you are trying to call allmatches[0] (trying to access the first element of an empty list) so here is your error

Remove bottom line on DateTimePicker

HelloEverybody,
I hope that you are doing well.
I am trying to remove bottom line on datetimepicker in Flutter but I do not find the solution. Some help would be greatly appreciated.
Many thanks.
Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(2.0, 2.0, 15.0, 1.0),
child: DateTimePicker(
decoration: InputDecoration(
border: InputBorder.none,
),
type: DateTimePickerType.dateTimeSeparate,
dateMask: 'd MMM yyyy',
controller: _controlerTaskDueDate,
//initialValue: DateTime.now().toString(),
firstDate: DateTime(2020),
lastDate: DateTime(2200),
icon: Padding(
padding: const EdgeInsets.fromLTRB(5.0, 4.0, 0.0, 1.0),
child: Icon(Icons.event),
),
dateLabelText: 'Due Date',
timeLabelText: "Due Time",
//use24HourFormat: false,
selectableDayPredicate: (date2) {
if (date2.weekday == 6 || date2.weekday == 7) {
return true;
}
return true;
},
onChanged: (valDueDate) => setState(() => _valueTaskDueDateChanged = valDueDate),
validator: (valDueDate) {
setState(() => _valueTaskDueDateToValidate = valDueDate);
return null;
},
onSaved: (valDueDate) => setState(() => _valueTaskDueDateSaved = valDueDate),
),
),
),
Within your DateTimePicker() widget, add this decoration:
decoration: InputDecoration(
border: InputBorder.none,
),
UPDATE:
Since my previous answer was overriding the DateTimePicker decorations, I found this to work:
Theme(
data: ThemeData(
inputDecorationTheme: InputDecorationTheme(
border: InputBorder.none,
)
),
child: //your card widget,
),

select initial value for a Dropdown button in flutter

I have searched similar answers to my question but non sufficed mine, Maybe my approach if difficult.
Here is what I'm trying to do,
I have an API for country values.
for registering the user I use a dropdown as below which maps the CountryApi values to its items.
In my hand I have the country name only.
how can I set the initial value of that dropdown to match with the country name I have?
Country Select DropDown
CountryModal _selectedCountry;
onChangeDropdownItem(CountryModel selectedCountry) {
setState(() {
// fieldFocusChange(context, _countryFocusNode, _phoneFocusNode);
_selectedUserCountry = selectedCountry;
_userCountry = _selectedUserCountry.name;
countryCodeTxt = _selectedUserCountry.dial_code;
countryCode = _selectedUserCountry.code;
_userCountryId = _selectedUserCountry.id.toString();
});
}
/////
userCountryDropdown = Container(
padding: EdgeInsets.all(2.0),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(
10.0,
)),
color: Color(0xFFF0F0F0),
shape: BoxShape.rectangle),
child: DropdownButton<CountryModel>(
isExpanded: true,
hint: Text(
'Select Country',
style: kMainContentStyleLightBlack,
),
autofocus: true,
value: _selectedUserCountry,
isDense: false,
onChanged: onChangeDropdownItem,
items: _countryList.map((country) {
return DropdownMenuItem<CountryModel>(
child: new Text(country.name),
value: country,
);
}).toList(),
style: kMainContentStyleLightBlack,
),
);
My Country API looks in this format
CountryAPI
[
{
"id": 1,
"code": "AF",
"dial_code": "+93",
"name": "افغانستان"
},
{
"id": 2,
"code": "AX",
"dial_code": "+358",
"name": "Åland"
},
....
Can someone help me to figure this out and workaround to resolve?
The value property is what specifies the default selected value for the drop down.
Container(
padding: EdgeInsets.all(2.0),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(
10.0,
)),
color: Color(0xFFF0F0F0),
shape: BoxShape.rectangle),
child: DropdownButton(
isExpanded: true,
hint: Text(
'Select Country',
style: kMainContentStyleLightBlack,
),
autofocus: true,
value: _selectedUserCountry,
isDense: false,
onChanged: onChangeDropdownItem,
items: _countryList.map((country) {
return DropdownMenuItem(
child: new Text(country.name),
value: country.name,
);
}).toList(),
style: kMainContentStyleLightBlack,
),
);
I didn't read your question much but here is the reference for you. dropdownValue is the default value here
Padding(
padding: EdgeInsets.all(8.0),
child: DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.arrow_drop_down),
iconSize: 24,
elevation: 16,
// style: TextStyle(color: Colors.white),
underline: Container(
height: 2,
width: double.infinity,
// color: Colors.deepPurpleAccent,
),
onChanged: (String newValue) {
setState(() {
print(newValue);
dropdownValue = newValue;
});
},
items: <String>['Male', 'Female']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),

Cant get data's length in itemCount

I want to get my API data's length to use in Listview.builder widget. I want to get my data from API which is 'mahalle'. And this data is a list of data. I want to get this data's length to build a list. But I got an error like this:
#4 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4546
...
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by widgets library ═══════════════════════════════════
Class 'Future<dynamic>' has no instance getter 'length'.
Receiver: Instance of 'Future<dynamic>'
Tried calling: length
The relevant error-causing widget was
MahalleList
And the reference of the error is :
#action
Future<void> fetcMahalle() async {
// var data =
// await httpClient.getData(globals.SELECT_URL).then((mahalle) => mahalle);
networkService.getMahalle();
}
And I'm getting my data with :
Future getMahalle() async {
BaseOptions options = new BaseOptions(
baseUrl: globals.PROD_URL,
connectTimeout: 5000,
receiveTimeout: 3000,
);
Dio dio = new Dio(options);
dio.options.headers["Authorization"] = "Bearer ${globals.USER_TOKEN}";
try {
var response =
await dio.get(globals.SELECT_URL); //'api/hizlirapor/selects'
List<MahalleModel> mahalleList = response.data['mahalle']
.map<MahalleModel>((mahalle) => MahalleModel.fromJson(mahalle))
.toList();
return mahalleList;
} on DioError catch (e) {
debugPrint("ERRORR!!!!!!!!!!!!! ${e.error.toString()}");
return null;
}
}
And finally here's the widget I'm trying to use my list data's length :
Container _buildBody(
BuildContext context, ObservableFuture<List<MahalleModel>> future) {
return Container(
color: backgroundColor,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_buildSearchBar(context),
RefreshIndicator(
onRefresh: mahalleStore.fetcMahalle,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: mahalleList.length,
itemBuilder: (context, index) {
final mahalle = mahalleList[index];
return Container(
height: 100,
child: Card(
color: Colors.white,
margin: EdgeInsets.all(15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
color: mainColor,
width: 3,
height: 50,
),
SizedBox(
width: 15,
),
Icon(
AppIcon.mahalle_raporu,
color: mainColor,
),
SizedBox(
width: 15,
),
Text(
mahalle.mahalleAdi,
style: textStyle,
),
],
),
),
);
}),
),
],
),
);
}
Thanks for your all help !
FutureBuilder(
future:mahalleStore.fetchMahalle,
builder: (context, snapshot){
//whatever returns from this function, will be avaliable inside snapshot paremeter.
final mahalleList= snapshot.data;
switch (snapshot.connectionState) {
case ConnectionState.waiting:
{
return Center(child: CircularProgressIndicator(),);
}
case ConnectionState.done:
if (snapshot.hasData) {
// do what you want here
}
return Text("Error occured");
default:
//
}});
I deleted previous comment , check this one.
You should use FutureBuilder because getMahalle returns a Future.

Flutter input decoration suffixIcon not appearing but always showing cross symbol

I have the following example codes. I have now managed to put the prefixicon and it works fine. I want to move the same icon the suffix meaning on the right hand side but it just does not work but the X symbol it what appears.
Here is a screen shot.
I have added the following lines suffixIcon: IconButton( but it seems not be appearing but the one on the left hand side which is the prefix appears perfectly fine. I cant get the one on the right hand side. What is blocking it from appearing?
Below is my codes.
class MyHomePageState extends State<MyHomePage> {
// Show some different formats.
final formats = {
//InputType.both: DateFormat("EEEE, MMMM d, yyyy 'at' h:mma"),
//InputType.date: DateFormat('dd/MM/yyyy'),
//InputType.time: DateFormat("HH:mm"),
InputType.date: DateFormat("d MMMM yyyy"),
};
//InputType.date: DateFormat('yyyy-MM-dd'),
// Changeable in demo
InputType inputType = InputType.date;
bool editable = true;
DateTime date;
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(appName)),
body: Padding(
padding: EdgeInsets.all(16.0),
child: ListView(
children: <Widget>[
Form(
//key: _myKey,
child: Column(
children : [
new Container(
width: 200.0,
child:new DateTimePickerFormField(
dateOnly: true,
format: formats[inputType],
editable: false,
validator: (val) {
if (val != null) {
return null;
} else {
return 'Date Field is Empty';
}
},
/*decoration: InputDecoration(
border: InputBorder.none,
labelText: 'From',contentPadding: const EdgeInsets.symmetric(horizontal: 20.0)),*/
decoration: InputDecoration(
hintText: 'To',
border: InputBorder.none,
filled: false,
prefixIcon: Icon(
Icons.arrow_drop_down,
color: Colors.blue,
size: 28.0,
),
suffixIcon: IconButton(
icon: Icon(Icons.arrow_drop_down,size: 28),
onPressed: () {
debugPrint('222');
})),
initialValue: DateTime.now().subtract(new Duration(days: 7)), //Add this in your Code.
),
)
]
),
),
RaisedButton(
onPressed: () {
/*if (_myKey.currentState.validate()) {
_myKey.currentState.save();
} else {
}*/ print("check;");
if(emailController.text.isEmpty){
print("TEST;");
//valid = false;
//emailError = "Email can't be blank!";
//openAlertBox();
Toast.show("Empty Date From", context, backgroundColor: Colors.red );
}
else{
print("not empty;");
final f = new DateFormat('yyyy-MM-dd');
final original = new DateFormat('d MMMM yyyy');
print("Format datre is"+emailController.text);
print("Formate date :"+original.parse(emailController.text).toString());
}
},
child: Text('Submit'),
)
],
),
));
I re-created your case by singling out only the TextFormField code you provided and was able to see the dropdown arrow as suffixIcon.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: MediaQuery
.of(context)
.size
.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children : [
new Container(
color: Colors.yellow,
width: 200.0,
child: TextFormField(
decoration: InputDecoration(
hintText: 'To',
border: InputBorder.none,
filled: false,
prefixIcon: Icon(
Icons.arrow_drop_down,
color: Colors.blue,
size: 28.0,
),
suffixIcon: IconButton(
icon: Icon(Icons.arrow_drop_down,size: 28),
onPressed: () {
debugPrint('222');
})),
),
)
]
)
),
)
);
}
}
I see that you used Padding as your body to return Scaffold. Try to replace it with Center or Container
You may have discovered the following but posting just in case...
The 'X' displayed is the reset icon for the date field ie. you use that to clear the field. You can turn it off with DateTimePickerFormField property 'resetIcon: null;' but then the only way to remove a date from the field is to ensure 'editable: true', which is the default but you have overridden it in your code.
Hope that helps.