Here is my requirement, when I click the Add button, dynamically new cards with three TextFields should be generated, and how to assign each TextField with dynamically created TextEditingControllers> or is there any other way to take value from TextFields?
final name1 = new TextField(
controller: name1Controller,
decoration: InputDecoration(
labelText: 'Full Name', border: OutlineInputBorder()));
final age1 = new TextField(
controller: age1Controler,
keyboardType: TextInputType.number,
decoration:
InputDecoration(labelText: 'Age', border: OutlineInputBorder()));
final studyjob1 = new TextField(
controller: study1Controller,
decoration: InputDecoration(
labelText: 'Study / Job', border: OutlineInputBorder()));
final person1Card = new Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
elevation: 10,
child: Padding(
padding: EdgeInsets.only(top: 2.0, left: 6.0, right: 6.0, bottom: 2.0),
child: Column(
children: <Widget>[
Text('Person 1'),
SizedBox(height: 3.0),
name1,
SizedBox(height: 10.0),
age1,
SizedBox(height: 10.0),
studyjob1,
SizedBox(height: 10.0),
],
),
),
);
return Scaffold(
appBar: AppBar(
title: Text('New Entry'),
),
body: SingleChildScrollView(
child: Container(
child: Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: <Widget>[
person1Card,
SizedBox(
height: 10.0,
),
saveButton
],
),
),
),
),
))
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text('Add entries'),
onPressed: () async {
List<PersonEntry> persons = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SOF(),
),
);
if (persons != null) persons.forEach(print);
},
),
),
);
}
}
class SOF extends StatefulWidget {
#override
_SOFState createState() => _SOFState();
}
class _SOFState extends State<SOF> {
var nameTECs = <TextEditingController>[];
var ageTECs = <TextEditingController>[];
var jobTECs = <TextEditingController>[];
var cards = <Card>[];
Card createCard() {
var nameController = TextEditingController();
var ageController = TextEditingController();
var jobController = TextEditingController();
nameTECs.add(nameController);
ageTECs.add(ageController);
jobTECs.add(jobController);
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Person ${cards.length + 1}'),
TextField(
controller: nameController,
decoration: InputDecoration(labelText: 'Full Name')),
TextField(
controller: ageController,
decoration: InputDecoration(labelText: 'Age')),
TextField(
controller: jobController,
decoration: InputDecoration(labelText: 'Study/ job')),
],
),
);
}
#override
void initState() {
super.initState();
cards.add(createCard());
}
_onDone() {
List<PersonEntry> entries = [];
for (int i = 0; i < cards.length; i++) {
var name = nameTECs[i].text;
var age = ageTECs[i].text;
var job = jobTECs[i].text;
entries.add(PersonEntry(name, age, job));
}
Navigator.pop(context, entries);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: cards.length,
itemBuilder: (BuildContext context, int index) {
return cards[index];
},
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: RaisedButton(
child: Text('add new'),
onPressed: () => setState(() => cards.add(createCard())),
),
)
],
),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.done), onPressed: _onDone),
);
}
}
class PersonEntry {
final String name;
final String age;
final String studyJob;
PersonEntry(this.name, this.age, this.studyJob);
#override
String toString() {
return 'Person: name= $name, age= $age, study job= $studyJob';
}
}
You can use a List for your controllers.
For example:
class PersonControllers {
final TextEditingController name;
final TextEditingController age;
final TextEditingController job;
PersonControllers(this.name, this.age, this.job);
}
Then in your widget
final List<PersonControllers> personControllers = List<PersonControllers>();
In your initState
personControllers.add(PersonController(TextEditingController(),TextEditingController(),TextEditingController());
Create a buildCard method:
Widget buildCard(PersonControllers controllers){
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
elevation: 10,
child: Padding(
padding: EdgeInsets.only(top: 2.0, left: 6.0, right: 6.0, bottom: 2.0),
child: Column(
children: <Widget>[
Text('Person 1'),
SizedBox(height: 3.0),
_buildNameField(controllers.name),
SizedBox(height: 10.0),
_buildAgeField(controllers.age),
SizedBox(height: 10.0),
_buildJobField(controllers.job),
SizedBox(height: 10.0),
],
),
),
);
}
Finally in your build method:
return Scaffold(
appBar: AppBar(
title: Text('New Entry'),
),
body: SingleChildScrollView(
child: Container(
child: Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: <Widget>[
...personControllers.map((personController) => _buildCard(personController),
SizedBox(
height: 10.0,
),
RaisedButton(
child: Text("Add"),
onPressed: (){
setState((){
personControllers.add(PersonController(
TextEditingController(),
TextEditingController(),
TextEditingController()
});
);
}
),
],
),
),
),
),
))
if you want remove the form also please look at this code may be it is help full for someone
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
var nameTECs = <int, TextEditingController>{};
var mailTECs = <int, TextEditingController>{};
List<Entry> entries = [];
var item = <int, Widget>{};
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
void didChangeDependencies() {
super.didChangeDependencies();
item.addAll({0: newMethod(context, 0)});
}
ondDone() {
entries.clear();
print(nameTECs.keys.last);
for (int i = 0; i <= nameTECs.keys.last; i++) {
var name = nameTECs[i]?.value.text;
var mail = mailTECs[i]?.value.text;
// print(mailTECs[i]?.value.text);
if (name != null && mail != null) {
entries.add(Entry(name, mail));
}
}
print(entries);
for (int a = 0; a < entries.length; a++) {
print(entries[a].name);
print(entries[a].email);
}
}
newMethod(
BuildContext context,
int index,
) {
var nameController = TextEditingController();
var mailController = TextEditingController();
nameTECs.addAll({index: nameController});
mailTECs.addAll({index: mailController});
return Column(
children: [
Text(index.toString()),
TextFormField(
controller: nameController,
validator: (value) {
return value!.isEmpty ? 'Enter some text' : null;
},
textFieldType: TextFieldType.NAME,
),
TextFormField(
controller: mailController,
validator: (value) {},
// controller: nameCount,
textFieldType: TextFieldType.NAME,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FlatButton(
onTap: () {
item.addAll({item.keys.last+1: newMethod(context, item.keys.last + 1)});
setState(() {});
// }
},
child: Text('Add'),
),
FlatButton(
onTap: () {
setState(() {
item.removeWhere((key, value) => key == index);
nameTECs.removeWhere((key, value) => key == index);
mailTECs.removeWhere((key, value) => key == index);
});
},
child: Text('Remove'),
),
],
),
],
);
}
#override
Widget build(BuildContext context) {
print('build');
return Scaffold(
appBar: AppBar(
title: Text('Test'),
),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: item.length,
itemBuilder: (context, index) {
return item.values.elementAt(index);
}),
// for (int i = 0; i < widgeta.length; i++) widgeta[i],
AppButton(
onTap: () {
if (_formKey.currentState!.validate()) {
ondDone();
// _formKey.currentState!.save();
setState(() {});
}
},
color: appPrimaryColor,
child: Text('save'),
),
Center(
child: Text('Test', textDirection: TextDirection.rtl),
),
],
),
),
),
);
}
}
class Entry {
final String? name;
final String? email;
Entry(
this.name,
this.email,
);
}
class _View4 extends StatefulWidget {
#override
_View4State createState() => _View4State();
}
class _GroupControllers {
TextEditingController name = TextEditingController();
TextEditingController tel = TextEditingController();
TextEditingController address = TextEditingController();
void dispose() {
name.dispose();
tel.dispose();
address.dispose();
}
}
class _View4State extends State<_View4> {
List<_GroupControllers> _groupControllers = [];
List<TextField> _nameFields = [];
List<TextField> _telFields = [];
List<TextField> _addressFields = [];
#override
void dispose() {
for (final controller in _groupControllers) {
controller.dispose();
}
_okController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("Dynamic Group Text Field2"),
),
body: Column(
children: [
_addTile(),
Expanded(child: _listView()),
_okButton(context),
],
)),
);
}
Widget _addTile() {
return ListTile(
title: Icon(Icons.add),
onTap: () {
final group = _GroupControllers();
final nameField = _generateTextField(group.name, "name");
final telField = _generateTextField(group.tel, "mobile");
final addressField = _generateTextField(group.address, "address");
setState(() {
_groupControllers.add(group);
_nameFields.add(nameField);
_telFields.add(telField);
_addressFields.add(addressField);
});
},
);
}
TextField _generateTextField(TextEditingController controller, String hint) {
return TextField(
controller: controller,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: hint,
),
);
}
Widget _listView() {
final children = [
for (var i = 0; i < _groupControllers.length; i++)
Container(
margin: EdgeInsets.all(5),
child: InputDecorator(
child: Column(
children: [
_nameFields[i],
_telFields[i],
_addressFields[i],
],
),
decoration: InputDecoration(
labelText: i.toString(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
),
)
];
return SingleChildScrollView(
child: Column(
children: children,
),
);
}
final _okController = TextEditingController();
Widget _okButton(BuildContext context) {
final textField = TextField(
controller: _okController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
border: OutlineInputBorder(),
),
);
final button = ElevatedButton(
onPressed: () async {
final index = int.parse(_okController.text);
String text = "name: ${_groupControllers[index].name.text}\n" +
"tel: ${_groupControllers[index].tel.text}\n" +
"address: ${_groupControllers[index].address.text}\n";
await showMessage(context, text, "Result");
},
child: Text("OK"),
);
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
child: textField,
width: 100,
height: 50,
),
button,
],
);
}
}
Related
I have a custom Text widget Named Dynamic_Game_Preview , and also a TextField.
I want the Dynamic_Game_Preview to be changed with the change of the TextField.
I used onChanged method for the TextField but all the letters are shown separately in the Dynamic_Game_Preview. How can I handle this changes to be applied in the same Dynamic_Game_Preview simultaneously with changing the TextField?
Here is my code:
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:pet_store/widgets/dynamic_game_preview.dart';
import 'main.dart';
class Dynamic_Game extends StatefulWidget {
const Dynamic_Game({Key? key}) : super(key: key);
#override
State<Dynamic_Game> createState() => _Dynamic_GameState();
}
class _Dynamic_GameState extends State<Dynamic_Game> {
TextEditingController nameController = TextEditingController();
List<String> names = [];
bool isLoading = false;
List<Dynamic_Game_Preview> dynamicList = [];
void initState() {
super.initState();
dynamicList = [];
names = [];
}
void addNames() {
if (names.length == 1) {
names = [];
}
names.add(nameController.text);
nameController.clear();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.indigo,
title: const Text('Dynamic Game'),
leading: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onTap: () {
// Navigator.pop(context);
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => const HomePage(),
),
(route) => false,
);
},
),
),
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Center(
child: Column(
children: <Widget>[
SizedBox(height: 20),
Container(
margin: const EdgeInsets.symmetric(horizontal: 30),
child: TextField(
controller: nameController,
onChanged: (value) {
setState(() {
addNames();
});
},
decoration: const InputDecoration(
labelText: 'Enter a Pet Name',
),
),
),
SizedBox(height: 10),
Flexible(
fit: FlexFit.loose,
child: ListView.builder(
shrinkWrap: true,
itemCount: names.length,
itemBuilder: (_, index) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 5.0, vertical: 3.0),
child: Dynamic_Game_Preview(nameController.text),
);
},
),
),
],
),
),
),
);
}
}
Problem
Your code clears the nameController:
void addNames() {
...
nameController.clear();
}
Then the code is trying to display nameController.text, which just got cleared:
Dynamic_Game_Preview(nameController.text)
Solution
Something along these lines should work:
itemBuilder: (_, index) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 5.0, vertical: 3.0),
child: Dynamic_Game_Preview(names[index]),
);
},
Last but not least
This probably is not needed:
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
I'm new to flutter.
I need to get product information through a form using flutter provider.
I can get one object(like String name value only). But when I add multiple parameters, it shows the following error.
Too many positional arguments: 1 expected, but 3 found.
This is the code I wrote.
Model class
class Item {
String itemName;
String description;
double itemPrice;
Item(this.itemName, this.description, this.itemPrice);
}
ChangeNotifier class
class ItemAddNotifier extends ChangeNotifier {
List<Item> itemList = [];
addItem(String itemName, String description, double itemPrice) {
Item item = Item(itemName, description, itemPrice);
itemList.add(item);
notifyListeners();
}
}
Add items
class AddItems extends StatelessWidget {
final TextEditingController _itemNameTextEditing = TextEditingController();
final TextEditingController _itemDescriptionTextEditing =
TextEditingController();
final TextEditingController _itemPriceTextEditing = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Kavishka'),
),
body: Container(
padding: EdgeInsets.all(30.0),
child: Column(
children: [
TextField(
controller: _itemNameTextEditing,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
hintText: 'Item Name',
),
),
SizedBox(
height: 20.0,
),
TextField(
controller: _itemDescriptionTextEditing,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
hintText: 'Item Description',
),
),
SizedBox(
height: 20.0,
),
TextField(
controller: _itemPriceTextEditing,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
hintText: 'Item Price',
),
),
SizedBox(
height: 20.0,
),
RaisedButton(
child: Text('ADD ITEM'),
onPressed: () async {
if (_itemNameTextEditing.text.isEmpty) {
return;
}
await Provider.of<ItemAddNotifier>(context, listen: false)
.addItem(
_itemNameTextEditing.text,
_itemDescriptionTextEditing.text,
_itemPriceTextEditing.text);
Navigator.pop(context);
},
),
],
),
),
);
}
}
Home Screen
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Kavishka'),
actions: [
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) {
return AddItems();
},
),
);
},
icon: Icon(Icons.add))
],
),
body: Container(
padding: EdgeInsets.all(30.0),
child: Column(
children: [
Consumer<ItemAddNotifier>(builder: (context, itemAddNotifier, _) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: itemAddNotifier.itemList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Text(
itemAddNotifier.itemList[index].itemName,
style:
TextStyle(fontSize: 20.0, color: Colors.black),
),
],
),
);
});
})
],
),
),
);
}
}
Main
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) {
return ItemAddNotifier();
},
child: MaterialApp(
home: Container(
color: Colors.white,
child: HomeScreen(),
),
),
);
}
}
It shows the error in Item item = Item(itemName, description, itemPrice); line.
If someone can help me to fix this issue.
Thank you.
I'm developing an application with flutter. But I cannot fix the buttons in the project. On my chat page, the button goes up. I'm new to the Flutter language, can you help me?
Hello, I'm developing an application with flutter. But I cannot fix the buttons in the project. On my chat page, the button goes up. I'm new to the Flutter language, can you help me?
Screenshot:
My Button Code :
class HomePage extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomePage> {
final _scrollController = ScrollController();
List<TabItem> tabItems = List.of([
new TabItem(Icons.home, "Anasayfa", Colors.blue),
new TabItem(Icons.message, "Sohbet Odası", Colors.orange),
new TabItem(Icons.person, "Profil", Colors.red),
]);
int seciliPozisyon = 0;
CircularBottomNavigationController _navigationController;
#override
void initState() {
super.initState();
_navigationController =
new CircularBottomNavigationController(seciliPozisyon);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
centerTitle: true,
title: Text("Crypto App"),
),
body: Stack(
children: <Widget>[
Padding(
child: bodyContainer(),
padding: EdgeInsets.only(bottom: 60),
),
Align(alignment: Alignment.bottomCenter, child: bottomNav())
],
),
);
}
Widget bodyContainer() {
String activeUserId =
Provider.of<AuthorizationService>(context, listen: false).activeUserId;
Color selectedColor = tabItems[seciliPozisyon].color;
switch (seciliPozisyon) {
case 0:
return HomeScreen();
break;
case 1:
return FriendlyChatApp();
break;
case 2:
return Profile(
profileId: activeUserId,
);
break;
}
}
Widget bottomNav() {
return CircularBottomNavigation(
tabItems,
controller: _navigationController,
barHeight: 60,
barBackgroundColor: Colors.white,
animationDuration: Duration(milliseconds: 300),
selectedCallback: (int selectedPos) {
setState(() {
seciliPozisyon = selectedPos;
});
},
);
}
}
ChatApp Code :
void main() {
runApp(
FriendlyChatApp(),
);
}
final ThemeData kIOSTheme = ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.grey[100],
primaryColorBrightness: Brightness.light,
);
final ThemeData kDefaultTheme = ThemeData(
primarySwatch: Colors.orange,
accentColor: Colors.orangeAccent,
);
String _name = '';
class FriendlyChatApp extends StatelessWidget {
const FriendlyChatApp({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ChatScreen(),
);
}
}
class ChatMessage extends StatelessWidget {
ChatMessage({this.text, this.animationController});
final String text;
final AnimationController animationController;
#override
Widget build(BuildContext context) {
return SizeTransition(
sizeFactor:
CurvedAnimation(parent: animationController, curve: Curves.easeOut),
axisAlignment: 0.0,
child: Container(
margin: EdgeInsets.symmetric(vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(right: 16.0),
child: CircleAvatar(child: Text(_name[0])),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_name, style: Theme.of(context).textTheme.headline4),
Container(
margin: EdgeInsets.only(top: 5.0),
child: Text(text),
),
],
),
),
],
),
),
);
}
}
class ChatScreen extends StatefulWidget {
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
final List<ChatMessage> _messages = [];
final _textController = TextEditingController();
final FocusNode _focusNode = FocusNode();
bool _isComposing = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: Theme.of(context).platform == TargetPlatform.iOS //new
? BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey[200]),
),
)
: null,
child: Column(
children: [
Flexible(
child: ListView.builder(
padding: EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, int index) => _messages[index],
itemCount: _messages.length,
),
),
Divider(height: 1.0),
Container(
decoration: BoxDecoration(color: Theme.of(context).cardColor),
child: _buildTextComposer(),
),
],
),
),
);
}
Widget _buildTextComposer() {
return IconTheme(
data: IconThemeData(color: Theme.of(context).accentColor),
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
children: [
Flexible(
child: TextField(
controller: _textController,
onChanged: (String text) {
setState(() {
_isComposing = text.isNotEmpty;
});
},
onSubmitted: _isComposing ? _handleSubmitted : null,
decoration: InputDecoration.collapsed(
hintText: 'Mesajınızı Buraya Yazınız:'),
focusNode: _focusNode,
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 4.0),
child: Theme.of(context).platform == TargetPlatform.iOS
? CupertinoButton(
onPressed: _isComposing
? () => _handleSubmitted(_textController.text)
: null,
child: Text('Gönder'),
)
: IconButton(
icon: const Icon(Icons.send),
onPressed: _isComposing
? () => _handleSubmitted(_textController.text)
: null,
))
],
),
),
);
}
void _handleSubmitted(String text) {
_textController.clear();
setState(() {
_isComposing = false;
});
var message = ChatMessage(
text: text,
animationController: AnimationController(
duration: const Duration(milliseconds: 700),
vsync: this,
),
);
setState(() {
_messages.insert(0, message);
});
_focusNode.requestFocus();
message.animationController.forward();
}
#override
void dispose() {
for (var message in _messages) {
message.animationController.dispose();
}
super.dispose();
}
}
You need to add :
resizeToAvoidBottomInset: false,
Here as shown:
Scaffold(
//here
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: Colors.black,
centerTitle: true,
title: Text("Crypto App"),
),
body: Stack(
children: <Widget>[
Padding(
child: bodyContainer(),
padding: EdgeInsets.only(bottom: 60),
),
Align(alignment: Alignment.bottomCenter, child: bottomNav())
],
),
);
}
Here using this whenever user will type something setting value to false will make keyboard overlap the bottom navigation bar.
Hope this is what you wanted to achieve.
I'm trying to check if the keyboard is visible after tapping on the TextFormField by calling:
if (MediaQuery.of(context).viewInsets.bottom != 0) {
...
}
but as soon as I have this MediaQuery call in my code, the Keyboard doesn't even open anymore after tapping on the TextFormField...
Edited:
This is what happens when tapping on the TextFormField:
I added the code of the page which causes this faulty behavior:
class LearnPage extends StatefulWidget {
final int topicId;
final String topicName;
LearnPage(this.topicId, this.topicName);
#override
_LearnPageState createState() => _LearnPageState();
}
class _LearnPageState extends State<LearnPage> {
final mainCaardIndex = ValueNotifier<int>(0);
PageController _mainCaardController;
PageController _inputCaardController;
List<CaardM> caards;
List<PageM> mainCaardList = [];
List<List<PageM>> inputCaardList = [];
List<List<TextEditingController>> textControllers = [];
Future<void> async_init() async {
List<CaardM> caardList =
await DatabaseProviderCaard.db.getCaards(widget.topicId);
caards = caardList;
setState(() {});
}
bool _keyboardIsVisible() {
return !(MediaQuery.of(context).viewInsets.bottom == 0.0);
}
#override
void initState() {
async_init();
_mainCaardController = PageController();
_inputCaardController = PageController();
super.initState();
}
#override
void dispose() {
_mainCaardController.dispose();
_inputCaardController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightBlue,
title: Center(
child: Text(
widget.topicName,
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
actions: [
!_keyboardIsVisible()
? IconButton(
icon: Icon(Icons.check_circle_outline),
tooltip: 'Validate',
onPressed: validate,
)
: IconButton(
icon: Icon(Icons.keyboard_hide),
onPressed: () {
FocusManager.instance.primaryFocus.unfocus();
},
),
],
),
body: Column(
children: [
Expanded(
flex: 3,
child: FutureBuilder(
future: getMainContent(),
builder: (context, AsyncSnapshot<int> snapshotMain) {
if (snapshotMain.connectionState == ConnectionState.done) {
return PageView.builder(
itemCount: snapshotMain.data,
controller: _mainCaardController,
onPageChanged: (position) {
mainCaardIndex.value = position;
mainCaardIndex.notifyListeners();
_inputCaardController.jumpToPage(0);
},
itemBuilder: (context, position) {
return LearnMainCaard(
mainCaardList[position].title,
mainCaardList[position].content,
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
),
Expanded(
flex: 5,
child: FutureBuilder(
future: getInputContent(),
builder: (context, AsyncSnapshot<int> snapshotInput) {
if (snapshotInput.connectionState == ConnectionState.done) {
return ValueListenableBuilder(
valueListenable: mainCaardIndex,
builder: (context, value, _) {
return PageView.builder(
itemCount: snapshotInput.data,
controller: _inputCaardController,
itemBuilder: (context, position) {
return LearnInputCaard(
inputCaardList[mainCaardIndex.value][position].title,
textControllers[mainCaardIndex.value][position],
);
},
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
),
],
),
);
}
Future<int> getMainContent() async {
List<PageM> caardPages;
mainCaardList.clear();
for (var i = 0; i < caards.length; i++) {
caardPages = await DatabaseProviderPage.db.getPages(caards[i].id);
if (caards[i].pageAmount > 1) {
mainCaardList.add(caardPages[0]);
}
}
return mainCaardList.length;
}
Future<int> getInputContent() async {
List<PageM> caardPages = [];
List<PageM> list = [];
inputCaardList.clear();
for (var i = 0; i < caards.length; i++) {
caardPages = await DatabaseProviderPage.db.getPages(caards[i].id);
if (caards[i].pageAmount > 1) {
addController(caards[i].pageAmount - 1);
list = [];
for (var i = 1; i < caardPages.length; i++) {
list.add(caardPages[i]);
}
inputCaardList.add(list);
}
}
return inputCaardList[mainCaardIndex.value].length;
}
void addController(int controllerAmount) {
List<TextEditingController> currentTextControllers = [];
print('addController called');
currentTextControllers.clear();
currentTextControllers = List.generate(
controllerAmount, (index) => TextEditingController()
);
textControllers.add(currentTextControllers);
}
And here the LearnInputCaard widget:
import 'package:flutter/material.dart';
class LearnInputCaard extends StatefulWidget {
final String title;
final TextEditingController textController;
LearnInputCaard(
this.title,
this.textController,
);
#override
_LearnInputCaardState createState() => _LearnInputCaardState();
}
class _LearnInputCaardState extends State<LearnInputCaard> {
#override
Widget build(BuildContext context) {
return Container(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
margin: EdgeInsets.all(20),
color: Colors.amberAccent.shade100,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Expanded(
flex: 1,
child: Text(
widget.title,
style: TextStyle(fontSize: 20),
),
),
Divider(color: Colors.black38,),
Expanded(
flex: 10,
child: Container(
padding: EdgeInsets.all(10.0),
child: TextFormField(
controller: widget.textController,
maxLines: 30,
decoration: InputDecoration(
hintText: "Enter content",
border: InputBorder.none,
),
),
),
)
],
),
),
),
);
}
}
you need to check MediaQuery.of(context).viewInsets.bottom == 0.0
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 Keyboard Visibility Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_keyboardIsVisible()
? Text(
"Keyboard is visible",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
)
: RichText(
text: TextSpan(children: [
TextSpan(
text: "Keyboard is ",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
),
TextSpan(
text: "not ",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.red),
),
TextSpan(
text: "visible",
style: Theme.of(context)
.textTheme
.display1
.copyWith(color: Colors.blue),
)
]),
),
SizedBox(
height: 20,
),
Container(
width: 200.0,
child: TextField(
style: Theme.of(context).textTheme.display1,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
),
borderRadius: BorderRadius.circular(10.0),
),
),
),
)
],
),
));
}
bool _keyboardIsVisible() {
return !(MediaQuery.of(context).viewInsets.bottom == 0.0);
}
}
The problem is that you get the context from the parent widget.
If you call:
MediaQuery.of(context);
in the same widget where your forms are, you shouldn't get this behavior.
You need to define a GlobalKey<FormState> in your highest widget and pass this one down. Then it works. I defined it first in my SafeArea and therefore it failed and I had the same problem with the keyboard.
Here are some snippets of my code. I have a PageController and use two different forms on my two pages.
class OnboardingScaffold extends HookConsumerWidget {
OnboardingScaffold({Key? key}) : super(key: key);
// here you define your GlobalKeys
final _formKeyLogin = GlobalKey<FormState>();
final _formKeyApply = GlobalKey<FormState>();
#override
Widget build(BuildContext context, WidgetRef ref) {
final controller = usePageController();
bool isKeyboard = MediaQuery.of(context).viewInsets.bottom != 0;
return Scaffold(
body: Container(
padding: !isKeyboard
? const EdgeInsets.only(bottom: 80)
: const EdgeInsets.only(bottom: 0),
child: PageView(
controller: controller,
children: [
// here you pass these keys into your child Widget
LoginSafeArea(
formKey: _formKeyLogin,
),
ApplySafeArea(
formKey: _formKeyApply,
),
],
),
),
bottomSheet: !isKeyboard
? Container(height: 80)
: Container(height: 0),
);
}
}
The child Widget should contain a Form Widget:
class LoginSafeArea extends HookConsumerWidget {
const LoginSafeArea({Key? key, required this.formKey}) : super(key: key);
final GlobalKey<FormState> formKey;
#override
Widget build(BuildContext context, WidgetRef ref) {
return SafeArea(
child: Center(
child: Form(
key: formKey,
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
children: <Widget>[
const EmailFieldWidget(),
const SizedBox(height: 8.0),
const PasswordFieldWidget(),
const SizedBox(height: 16.0),
LoginButtonWidget(
formKey: formKey,
),
const SizedBox(height: 8.0),
],
),
),
),
),
);
}
}
I am trying to render a search form above a list of items from Firestore and filter locally based on what is typed in the form.
I tried adding both widgets to the body like this, but it is only displaying the search form:
body: Column(
children: <Widget>[Searchform(), ContentWidget()],
),
This is the current code which displays a basic list:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Items extends StatefulWidget {
Items({Key key}) : super(key: key);
#override
_ItemsState createState() => _ItemsState();
}
class _ItemsState extends State<Items> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Search'),
),
body: ContentWidget(),
);
}
}
class Searchform extends StatelessWidget {
final TextEditingController _searchController = TextEditingController();
#override
Widget build(BuildContext context) {
return TextField(
controller: _searchController,
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15.0),
),
),
),
);
}
}
class ContentWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('content').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['term']),
);
}).toList(),
);
}
},
);
}
}
What I was thinking of doing is saving the items in local state and filter them based on what is typed in the search box.
this is a very simple way try this code within "snapshot.data.documents.map((DocumentSnapshot document)"
if(_searchController.text.toString().contains(document['term'])){
return new ListTile(
title: new Text(document['term']),
);
}
I have provide simple filter record in listview code.
class FilterDemo extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return FilterState();
}
}
class FilterState extends State<FilterDemo> {
List<String> items, duplicateList;
TextEditingController editingController = TextEditingController();
#override
void initState() {
// TODO: implement initState
super.initState();
items = List<String>.generate(1000, (i) => "Item $i");
duplicateList = items;
}
void filterSearchResults(String query) {
List<String> dummySearchList = List<String>();
dummySearchList.addAll(duplicateList);
if (query.isNotEmpty) {
List<String> dummyListData = List<String>();
dummySearchList.forEach((item) {
if (item.contains(query)) {
dummyListData.add(item);
}
});
setState(() {
items.clear();
items.addAll(dummyListData);
});
return;
} else {
setState(() {
items.clear();
items.addAll(duplicateList);
});
}
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Filter Demo"),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) {
filterSearchResults(value);
},
controller: editingController,
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)))),
),
),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
);
},
),
),
],
),
);
}
}
I have provide Code how the saving the items in local state and filter them based on what is typed in the search box.
class UserList extends StatefulWidget {
final FirebaseUser user;
final String currentUserId;
UserList({this.currentUserId, this.user});
#override
_UserListState createState() => _UserListState();
}
class _UserListState extends State<UserList> {
TextEditingController _signUpConfirmPassword = new TextEditingController();
String _myValue = '';
UniqueKey _myKey = UniqueKey();
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("UserList"),
),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Padding(
padding: EdgeInsets.all(10.0),
child: CupertinoTextField(
keyboardType: TextInputType.text,
//inputFormatters: [LengthLimitingTextInputFormatter(60)],
placeholder: 'Search For..',
// placeholderStyle: TextStyle(
// fontWeight: FontWeight.w200
// ),
prefix: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Icon(
Icons.search,
),
),
onChanged: (val) {
if (val.isNotEmpty) {
_myValue = val;
}
setState(() {
_myKey = UniqueKey();
});
},
decoration: BoxDecoration(
border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(20.0)),
)),
SizedBox(height: 10.0),
Container(
key: _myKey,
child: FetchUsers(
user: widget.user,
myValue: _myValue,
)),
],
));
}
}
class FetchUsers extends StatefulWidget {
final String myValue;
final FirebaseUser user;
FetchUsers({this.myValue, this.user});
#override
_FetchUsersState createState() => _FetchUsersState();
}
class _FetchUsersState extends State<FetchUsers> {
List searchName = List();
List userName = List();
Future listOfUsers() {
if (widget.myValue.isEmpty) {
return Firestore.instance
.collection('users')
.where('Role', isEqualTo: 'user')
.orderBy('Created', descending: true)
.limit(10)
.getDocuments()
.then((d) {
userName.clear();
d.documents.forEach((f) {
userName.add(f);
});
return userName;
});
} else {
return Firestore.instance
.collection('users')
.where('Role', isEqualTo: 'user')
.limit(10)
.getDocuments()
.then((d) {
searchName.clear();
d.documents.forEach((f) {
if (f.data['Name']
.toString()
.toLowerCase()
.contains(widget.myValue.toLowerCase())) {
searchName.add(f);
}
});
return searchName;
});
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: listOfUsers(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CupertinoActivityIndicator(),
);
} else {
return ListView.separated(
physics: ClampingScrollPhysics(),
separatorBuilder: (context, int) {
return Divider();
},
itemCount: snapshot.data.length,
shrinkWrap: true,
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) {
return Card(
elevation: 7.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
' ${snapshot.data[index]['Name']}',
style: TextStyle(
color: outlineColor,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 5.0,
),
Text(
' ${snapshot.data[index]['Email']}',
),
],
),
Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
RaisedButton.icon(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
color: primaryColor,
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => Chat(
user: widget.user,
name: snapshot.data[index]
['Name'],
peerId: snapshot.data[index]
['UID'],
)));
},
icon: Icon(
Icons.chat,
color: themeColor,
),
label: Text(
"Chat",
style: TextStyle(color: themeColor),
)),
RaisedButton.icon(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
color: primaryColor,
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) =>
SendNotificationOption(
name: snapshot.data[index]
['Name'],
myFcm: snapshot.data[index]
['UID'],
isBroadcast: false,
)));
},
icon: Icon(
Icons.notifications,
color: themeColor,
),
label: Text(
"Notification",
style: TextStyle(color: themeColor),
)),
],
),
],
),
),
));
},
);
}
},
);
}
}
What you have type in Search then that Data is shown in listview]1